[MSSQL] Excel 匯入資料庫後 ntext 型態在第 255 字被截斷

這篇是我搬運自己以前在 Medium 上 2020年 4 月的文章,算是在第一次嘗試寫和工作有關的文章,非常具有紀念價值XD 當時這麼沒有修飾的文章,後台數據竟然還不錯,懷疑我寫完後被我同事搜尋到哈哈哈(Medium 的 SEO 分數好像蠻厲害的)。到現在還是很想念那一間公司,不論主管或同事都讓我成長好多,很感恩那裡的一切。

最近要開始慢慢搬家,原本只搬運我另外一個網站的文章,突然想到這裡也有文章,就順便集合在一起了,也順便潤飾字句以便閱讀體驗更佳。


發現問題

問題點就如標題。當時透過 OleDb 寫 Excel 匯入資料庫的 C# 程式,發現 ntext 型態的欄位中有的會截斷在255 字,而有的不會。

似乎沒有太多網友遇到這種問題,不論怎麼下關鍵字都找不太到想要的文章。有相同狀況的網友,也只會在底下收到:

「要不要設成NTEXT試試看?」(答非所問)
「NTEXT是不限字數的,一定是程式哪邊寫錯」(可能性較大,但這次很確定不是)

後來發現是關鍵字下不夠精準的問題,用「前八列」、「excel」、「匯入」可以找到一些討論此問題的文章。不過一開始完全不會知道有前八列的規則,所以找得很辛苦。

況且如果知道關鍵字要下「前八列」大概也表示知道原因了吧XD 用英文搜索資料會更多,有個大概十幾年前的文章,大意是說遇到比較長的字串,會自動截斷以節省顯示時間,實際上數據是完整的。不過我的情況也非如此,取資料出來看,確實是真的被截斷了。

可能成因

經過篩選後,這是可能性最大的原因:電腦讀取excel時會自動以前8列來判定每列的數據類型。

例如說前 8 列都是數字,後面欄位將會以數字型態處理(出現非整數即回傳 Null),如果前8列都是日期,那便會以日期格式去處理。然而字串又更特別一些,假如前8列皆是字串且沒有超過 255 個字,就會被設置為 nvarchar(255),也就是說超過 255 個字就會被截斷,即使你當初寫程式時是以 ntext 型態匯入。

或許這對常處理 Excel 資料的人來說是基本觀念,但對於終於以拙劣的搜尋技巧查詢到這個可能性的我來說真的是大開眼界啊!

來實驗看看

基於好奇心,我做了一些簡單的匯入測試:表格內容只有四種內容:數字空格1 個字400 個字,並且不變動 Excel 本身的儲存格格式。觀察前 8 列的型態,對照第 12 列資料(都是 400 字)的匯入情形,並將匯入情形用顏色標示。(數字都可以正常匯入,所以並未列入觀察範圍)

紅色是完全沒匯入,黃色是有匯入但是被截斷(255字),綠色就是正常。

test1-關於 num4 那一欄我應該是標錯了,因為後續測試是匯不成功的(標示紅色)

依照結果試著推測看看匯入的欄位被判定成什麼型別,例如:

  • num1:第一列為不足255字的字串,其它為數字。所以型態可能被判斷為 nvarchar(255)。第 12 列資料在 255 字處被截斷。
  • num2:第一列為 400 字的字串,其它為數字,混合型態加上字數 400,被欄位可能判定成數字格式所以才回傳 NULL?這一欄位非常令人感到疑惑。第 12 列資料完全沒被匯入。
  • num3:前八列都是不足 255 字的字串,型態可能判斷為 nvarchar(255)。第 12 列資料在255字處被截斷。

其他欄大家可以自行推測,可以觀察到前 8 列的確是有關鍵元素在影響匯入。我陸續又做了幾個實驗:

test2
test3-用八個欄位的 400 字混合數字,除了第一欄以外其它沒匯成功
test4-和test3不同的是,欄位全部都是字
test5-像是test3和test4的混合版
test6-和前面實驗或許有些重複,但既然都得測試就都玩玩看
test7-與test6對照,都是字的狀況。

歸納出來,應該是這樣:

都是非數字:
(1) 前八列皆少於 255 字,判斷為 nvarchar(255),後續若超過 255 字將會截斷。
(2) 前八列只要有一個大於 255 字,匯入完整。

混合狀況:
(1) 前八列有小於 255 字 + 數字,匯入完整。
(2) 前八列大於 255 字 + 數字,超過 255 字欄位無法匯入。
(3) 前八列大於 255 字 + 小於 255 字,超過 255 字將會截斷。
(4) 前八列大於 255 字 + 小於 255 字 + 數字,超過 255 字將會截斷。

以上是我想到能歸納的可能性測試,或許對和我一樣有如進大觀園的網友會有理解上的幫助。

解決方式

既然找出問題了,還是得想辦法解決。

更改機碼

在網路上找到的方式主要都是改機碼,不過如果匯入大量資料的話其實效能蠻差的。

  1. 在工作列中的搜尋列打「regedit」,開啟「登錄編輯程式」。
  2. 輸入或點擊位置:(以下是我的電腦的位置,每台應該不太一樣)
    HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet\4.0\Engines\Excel
  3. 有兩個項目要設定:
    ImportMixedTypes:預設值是 Text,表示如果讀取的 Excel 欄位型態不同,將會自動轉成文字。
    TypeGuessRows:預設值是 8,也就是先前提到的以前 8 列為判斷依據。

也就是說,我們把 TypeGuessRows 的值改成 0 就可以了(點兩下可以看到編輯視窗),改成 0 之後會讀取所有欄位才決定資料應該用什麼格式,先前提到效能會變很差就是這個原因。

另外我還更動了以下兩個位置的機碼,天真地想說之後也許會用到:

\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office\14.0\Access Connectivity Engine\Engines\Excel

\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\14.0\Access Connectivity Engine\Engines\Excel

每台電腦可能不太一樣,可以稍微找一下。如果事情到這邊已經解決了那恭喜你;如果沒任何效果,那表示我們同病相憐,請再接著往下看吧XD

土法煉鋼

上一種方式對我的資料沒有用QQ 目前我是還沒找到其他聰明的方式,這裡提供兩個較笨的方法:

  1. 前八列先插入符合型態的資料
  2. 在每一格前面加上單引號「’」

第一點用白話講就是在前八列插入一堆垃圾資料讓 Excel 判定型別,匯入成功後,再手動刪除資料庫的資料;或是在寫匯入程式時從第九列開始讀取之類的,有跟我一樣改機碼也沒用的人試試倒也無妨。第二點是一般直接將儲存格轉換文字格式的方法,或是右鍵看儲存格內容更改也是可以,但太搞剛所以我沒有這樣做。

因為會違反「不動到資料」的原則,所以一開始相當抗拒,但畢竟資料匯入的完整性還是重點,所以在動資料記得要備份原始檔。另外會比較喜歡加入前八列而不是直接更改儲存格格式,是因為在我的觀念裡,加入的動作比較不會動到原資料,但更改格式就一定是拿原資料開刀,意外比較容易有。都是必須動到資料的前提下,我會選擇對我來說風險比較小的動作。


參考資料

讓我知道你在想什麼!

Picture of ALEX

ALEX

藍白拖愛好者,一事無成話偏多

Recent Posts

C++

NLP