2011-04-25

SSIS:使用for迴圈容器實現逐筆判斷更新或新增資料

        使用SSIS,我們可以很方便的匯整多方來源資料並匯入一資料庫中。但是,如果遇到來源資料有重覆需要判斷是否更新或直接新增時,那就有點苦惱了…
        一開始的想法,是將資料先分析好,將更新的擺一塊、新增的擺一塊,然後再分別作更新或新增的動作;但是這樣的作法,讓資料庫轉了兩次,直覺浪費許多時間及效能。於是開始思考有沒有可能在SSIS裡逐筆判斷該資料是需要更新或新增?費了好一些時間,終於讓我給試出來了!

作法:
因我們SSIS會利用變數來帶入每一筆資料來做邏輯判斷,所以這邊的SQL指令都必需先做成預存程序(STORED PROCEDURE)
                                                      
select_test,來源資料庫 

decdata,目的資料庫

1.首先,我們先新增一個和來源資料庫一模一樣的空資料庫,用來存放準備更新或新增的資料。在這邊,我們將select_test當作是來源資料庫,select_test_2則是空資料庫,decdata則是目的資料庫。

2.接著,我們開始設計SSIS模型;首先,設定好連接管理員,分別建立來源資料庫連接(SOURCE_DB)及目的資料庫連結(DEST_DB)。方法如下:

   2-1.在連接管理員欄位的空白處按下滑鼠右鍵→新增OLE DB連接。

    2-2.點選「新增」鈕。  

    2-3.選取資料庫路徑來源,設定帳號、密碼並選取資料庫名稱,按下「確定」鈕。

    2-4.最後按下「確定」鈕,完成設定。

3. 在流程控制中,拉取工具箱元件,完成下列設計。

4.在流程控制空白處,按下澤鼠右鍵→變數,新增以下4個變數:
    CHECK_TRANS_COUNT:用來記錄資料筆數。
    SOU_age、SOU_first_name、SOU_PLAN_CODE:記錄來源資料庫的資料欄位。

5.在流程控制,「執行SQL工作中」按下滑鼠右鍵→編輯

    5-1.在執行SQL工作編輯器左手邊,選取「一般」,編輯SQL陳述式。
           將Connection指向我們的來源資料庫連結(SOURCE_DB),      
           SQLstatement則是執行我們預先寫好的預存程序:CHECK_TRANS_DATA,它將會幫我們將來源資料庫的資料存進select_test_2資料庫中,並且幫我們計算有多少筆資料。
        CREATE PROCEDURE [dbo].[CHECK_TRANS_DATA]
        AS
            TRUNCATE TABLE select_test_2
            INSERT INTO select_test_2(PLAN_CODE,first_name,age)
            SELECT S.PLAN_CODE, S.first_name, S.age          
            FROM select_test as S
            SELECT COUNT(*) as RESULT FROM select_test_2



    5-2.在「結果集」,我們設定將執行預存程序所得到的資料筆數結果存進「CHECK_TRANS_COUNT」變數中,按下「確定」。

6.接下來,在for迴圈容器中,我們建立了三個「執行SQL工作」元件,依序命名為:

                    「選取第一筆要新增或更新的資料列」
                    「判斷並執行新增或更新資料」
                    「刪除選取的資料列」
        在for迴圈容器中,我們開始針對單一筆資料去做邏輯分析;由於資料庫如果沒有給明確的條件的話,沒辦法明白我們所謂的第N+1筆到底是哪筆資料,所以我設計的方式,是在執行for迴圈時,都去找第一筆資料,然後在邏輯分析執行完該筆資料後,刪除這筆資料,再執行下一次迴圈。這就是為什麼我們一開始我們要建立一個新的暫存來源資料庫select_test_2。
7.首先,先來設定for迴圈的邏輯:
    7-1.在for迴圈容器上,按下滑鼠右鍵→編輯

    7-2.在for迴圈邏輯器中,選擇左手邊「For迴圈」,開始編輯右手邊的「For迴圈屬性」
           一般在程式中我們編輯for迴圈的指令是這樣:

               for ( int i=0; i>0; i--)

          而For迴圈屬性中:

               InitExpression:建立一個for迴圈所需變數,即為程式中 int i
                                        在這邊,我們指定負責存放筆數數量的CHECK_TRANS_COUNT
              
               EvalExpression:for迴圈條件判斷,即為程式中的 i>0
                                         在這邊,我們的條件為CHECK_TRANS_COUNT>0
              
               AssignExpression:for迴圈的變數運算,即為程式中 i--
                                             在這邊,我們的變數運算為:
                                             CHECK_TRANS_COUNT=CHECK_TRANS_COUNT-1

    完成設定後,按下「確定」鈕。


8.選取「選取第一筆要新增或更新的資料列」,按滑鼠右鍵→編輯


    8-1. 在執行SQL工作編輯器左手邊,選取「一般」,編輯SQL陳述式。
            將Connection指向我們的來源資料庫連結(SOURCE_DB),      
            SQLstatement則是輸入以下指令:
            SELECT TOP 1 PLAN_CODE as resultA,first_name as resultB,age as resultC
            FROM select_test_2


    8-2.在執行SQL工作編輯器左手邊,選取「結果集」, 按「加入」,將我們查詢到的資料,
          個別放入先前設好的三個變數(SOU_age、SOU_first_name、SOU_PLAN_CODE)中;
          設定完成,按下「確定」鈕 。        


9.選取「判斷並執行新增或更新資料」」,按滑鼠右鍵→編輯


    9-1.在執行SQL工作編輯器左手邊,選取「一般」,編輯SQL陳述式。
            將Connection指向我們的目的資料庫連結(DEST_DB),      
            SQLstatement則是輸入:EXEC CHECK_UPorINSERT_DATA ?,?,?
            請注意!!這邊的變數用 ? 來取代,我們在下一步驟會繼續說明。

            這支預存程序 CHECK_UPorINSERT_DATA ,它會幫我們判斷該筆資料是該「新增」
            至目的資料庫,或該「更新」目的資料庫裡的資料。
           
            預存程序:CHECK_UPorINSERT_DATA的SQL指令碼如下:
            CREATE PROCEDURE [dbo].[CHECK_UPorINSERT_DATA]
                @PLAN_CODE      varchar(5)
               ,@first_name  varchar(10)
               ,@age   int
            AS
                 DECLARE @ROWCNTS INT
                 SELECT @ROWCNTS = COUNT(*)
                 FROM decdata
                 WHERE PLAN_CODE=@PLAN_CODE
                 IF @ROWCNTS > 0
                     Begin
                         UPDATE decdata SET first_name=@first_name,age=@age
                         WHERE PLAN_CODE=@PLAN_CODE
                     End
                 ELSE
                     Begin
                         INSERT INTO decdata (PLAN_CODE,first_name,age)
                         VALUES(@PLAN_CODE,@first_name,@age)
                     End


    9-2.在執行SQL工作編輯器左手邊,選取「參數對應」, 按「加入」,把我們步驟在8-2,
           儲存在SSIS變數(SOU_age、SOU_first_name、SOU_PLAN_CODE)依序放進來。
           參數名稱這個屬性,就是指要將值放進第幾個 ? 裡(由0開始起算!設定時要注意!)
           設定完成,請按下「確定」鈕。


10.選取「刪除選取的資料列」」,按滑鼠右鍵→編輯


    10-1.在執行SQL工作編輯器左手邊,選取「一般」,編輯SQL陳述式。
            將Connection指向我們的來源資料庫連結(SOURCE_DB),      
            SQLstatement則是輸入:EXEC DELETE_DATA ?,?,?

            預存程序:DELETE_DATA,是用來將判斷完的資料刪除,以便SSIS的迴圈開始讀下一筆資料。

             CREATE PROCEDURE [dbo].[DELETE_DATA]
                  @PLAN_CODE      varchar(5)
                 ,@first_name  varchar(10)
                 ,@age   int
             AS
             DELETE FROM select_test_2
             WHERE PLAN_CODE=@PLAN_CODE and first_name=@first_name and age=@age


    10-2.在執行SQL工作編輯器左手邊,選取「參數對應」, 按「加入」,把我們步驟在8-2,
           儲存在SSIS變數(SOU_age、SOU_first_name、SOU_PLAN_CODE)依序放進來。
           參數名稱這個屬性,就是指要將值放進第幾個 ? 裡(由0開始起算!設定時要注意!)
           設定完成,請按下「確定」鈕。


最後,執行SSIS,即可將資料逐筆分析該更新或是新增的結果。

2011-03-31

MS-SQL:查詢資料庫所有table的筆數、大小

在Google大神及網路上各先拜的幫助下,新學會以下兩個MS-SQL內建的預存程序:

查詢資料庫所有table的筆數:

    EXEC sp_MsForEachTable 'SELECT ''?'', COUNT(*) FROM ?'

查詢資料庫所有table的大小:

    EXEC sp_MsForEachTable "Sp_SpaceUsed '?'"

p.s.以上的那些問號,系統在執行時會辨別成當下所使用的資料庫名稱

2011-03-30

MS-SQL:將資料分組並比較資料大小

通常,我們將資料分組並且比較大小(如圖一整理成圖二),可以結合ORDER BY、GROUP BY,一個語法簡單搞定。如下所示:

                                               《圖一.資料原始檔》


原始資料如下:
    select title,val,note from data_compare

分組並按大→小排列:
    SELECT title,val,note FROM data_compare
    ORDER BY title, val desc

                                               《圖二.整理後資料》

但如果是需要用其它文字代替比較出來的結果(如圖三所示),那可能有點麻煩…


                                             《圖三.用其它文字代替比較出來的結果》

使用cursor一筆一筆處理解決似乎是不錯的辦法,但考慮到日後資料量變得龐大及其轉檔效能,這就變成最後的手段了。
在此提供另一種方式:
構想:
    1.先將各組最大值及最小值分別取出放入暫存table#A、#B
    2.排除最大值及最小值取出剩餘資料,即為中間值
    3.將要替代的文字一一放入最大值、最小值、中間值資料表,UNION ALL,完成!
程式碼如下:

SELECT distinct title, maxval=MAX(val)
into #A
FROM data_compare
GROUP BY title

SELECT distinct title, minval=MIN(val)
into #B
FROM data_compare
GROUP BY title

SELECT distinct data_compare.title, '棒' as val, data_compare.note
FROM data_compare
JOIN #A ON data_compare.title=#A.title
JOIN #B ON data_compare.title=#B.title
WHERE data_compare.val=#A.maxval and data_compare.title=#A.title
UNION ALL
SELECT distinct data_compare.title, '不錯' as val, data_compare.note
FROM data_compare
JOIN #A ON data_compare.title=#A.title
JOIN #B ON data_compare.title=#B.title
WHERE (data_compare.val<>#A.maxval and data_compare.title=#A.title) and (data_compare.val<>#B.minval and data_compare.title=#B.title)
UNION ALL
SELECT distinct data_compare.title, '加油' as val, data_compare.note
FROM data_compare
JOIN #A ON data_compare.title=#A.title
JOIN #B ON data_compare.title=#B.title
WHERE data_compare.val=#B.minval and data_compare.title=#B.title

2011-03-20

JAVA 關於遞增/遞減 運算子的前置與後置

前置:++a ;
後置:a++ ;
就變數運算結果看來,a++與++a,所得到的值完全相同,那麼,為什麼要分這兩種呢?
因為在程式執行,產生的結果並不相同;
舉例:宣告兩個整數變數a和b,並初始化,如下所示:
             int a=1 ;
             int b=1 ;
接下來,我們在程式運算時,分成兩種情況:

1.若指定:
             b = a++ ;
   再讓系統分別顯示變數 a、b的值,得到的結果如下:
             a = 2;b = 1。
   即運算中,由於變數 b 在“=“的右方會先碰到變數a,所以此時的 “=” 會系統解讀成「指
   向」的作用,所以變數 b被指向成了變數 a(此時變數a還沒做任何運算),所以:
             b = a = 1;
   而後,變數 a再行遞增運算:a = a+1 = 2。

2.若是我們指定:
             b= ++a ;

    再讓系統分別顯示變數 a、b的值,得到的結果如下:
             a = 2;b = 2。
    即運算中,由於變數 b 在“=“的右方會先碰到遞增運算式「++」,所以此時的 “=” 會系統
    解讀成「運算」,所以:
             b = a+1 = 2。

2011-03-08

JAVA 變數設定

變數宣告
  • 語法
    資料型態  變數名稱
    • //方式1 先定義型態,再定義初始值
      int x;
      x = 100
      //方式2 宣告同時初始化變數
      int y = 200;
  • 命名規則
    • 1.英文 or $ 開頭
      2.不得為java關鍵字
      3.字母大小寫有別
      4.可 [英文] + [數字] + [ _ ] 來命名;ex:String car_1;