2011年7月17日 星期日

[SSIS]輸出到文字檔,小數值0.123456的開頭0消失了的問題

  情況是這樣的,我利用SSIS從SQL Server輸出資料到文字檔,再轉給R6用,資料流程中我直接用[OLE DB來源]直接輸出到[一般檔案目的地],結果卻遇到怪事了,就是來源資料表中有幾個欄位是numeric的格式,這幾個欄位裡面有些值剛好介於0到1之間,假設為0.123456好了,轉換過程中並沒有出錯,但直接開啟輸出後的文字檔卻發現,開頭的0都不見了,變成.123456喔,大於1的值都正常呢,很怪吧
  網路上有看到有人有一樣的問題,Decimal Formatting from OLEDB Source to Flat File ,但沒有明確的解答,於是我做了一下測試,發現一些奇怪的事,在資料庫引擎端,NUMERIC的功能幾乎等於於DECIMAL,但在Intergration Services,DT_NUMERIC與DT_DECIMAL兩個差異倒是滿大的喔
  首先我產生幾筆測試資料,這測試資料是我參考別人所建的,如下圖(一),資料表有三個欄位,欄位Num_decimal的格式為DECIMAL(10,6),Num_numeric的為NUMERIC(10,6),另一個欄位為說明
圖(一)
  IS封裝的資料流程如下圖(二),兩個衍生資料行分別處理Num_decimal與Num_numeric欄位,之後發現在輸出結果一樣後,有做調整,這待會再看
圖(二)

  左邊的衍生資料行內容如下圖(三),針對Num_decimal欄位作資料型態轉換,轉成貨幣[DT_CY]數值[DT_NUMERIC]十進位[DT_DECIMAL]字串[DT_STR]及Unicode字串[DT_WSTR],右邊的衍生資料行內容幾乎一樣,差別只是針對的是Num_numeric欄位囉,就不另外截圖了
圖(三)
  轉換過程中,我用資料檢視器觀看緩衝區的資料當來源來說,然後跟最後的產出文字檔結果作對比,如下圖(四),在這個階段,兩個衍生資料行的測試結果都一樣,所以我只截其中一畫面表達
  1. Num_decimal欄位 --> Num_decimal欄位 來源格式為[NUMERIC],目的地格式為數值 [DT_NUMERIC] 0.多的0不見了,小數位數都一樣(Num_numeric欄位轉換結果相同,這應可說明在資料庫引擎端NUMERIC還是DECIMAL是沒差別的)
  2. DT_CY欄位 --> DT_CY欄位 來源目的地格式皆為貨幣 [DT_CY] 0.多的0有保留,但會去掉小數位皆為0的
  3. DT_NUMERIC(10,6)欄位 --> DT_NUMERIC(10,6)欄位 來源目的地格式皆為數值 [DT_NUMERIC] 與第一項的結果同
  4. DT_DECIMAL(6)欄位 --> DT_DECIMAL(6)欄位 來源目的地格式皆為十進位 [DT_DECIMAL] 0.多的0有保留,但小數點以下皆為0又去掉了(這與第三項DT_NUMERIC結果相反,這應可說明在IS端差很大)
  5. DT_STR(11)欄位 --> DT_STR(11)欄位 來源目的地格式皆為字串 [DT_STR] 雖在緩衝區時,0.多的0已經去掉了,但忠實還原結果到文字檔,來源目的結果一樣
  6. DT_WSTR(11)欄位 --> DT_WSTR(11)欄位 來源目的地格式皆為Unicode 字串 [DT_WSTR] 與第五項同
圖(四)

  從圖(四)的幾個結果比較下來,字串轉字串的結果(第五項第六項)比較像是我們需要的,但在緩衝區中有小數點的這種值,在數值轉文字的過程中會去掉0.多的0,那第四項轉DT_DECIMAL的,似乎不會去掉0,但會去掉小數點之後皆為0的,那我把兩種結合呢?先轉成DT_DECIMAL再轉成DT_STR看看,試試囉,如下圖(五)

圖(五)

  結果比較如下圖(六 ),這樣結果跟第四項又一樣了,看來還是不行

  這樣奇怪的現象我在SSIS 2008 R2試的結果也一樣,這應該算bug吧!那到底要如何保留開頭的0及保留有效的小數位數呢?
  1. How to Import Negative Decimal or Floating Values to Flat File Without Losing Leading Zero,此篇提供一種解法,得用到IS的Expression
  2. 第二種方法較簡單,既然在資料流程中資料轉換會出問題,那我就在資料庫引擎這端就先將數值轉成字串就好了啦,像我這個例子,
   原先資料來源用的是
   select Num_decimal,Num_numeric,cComment from test
   改成
   select cast(Num_decimal as varchar(11)) Num_decimal,cast(Num_numeric as varchar(11))   Num_numeric,cComment from test

   就搞定啦

0 意見:

張貼留言