同事回報一個鬼問題,某段古老 ASP.NET 程式使用 OLEDB 讀取 Excel/CSV 檔案(寫法範例,屬民國初年流行做法,現多會改用 EPPlus或 ClosedXML),原本運作良好,自從 Windows 2003 EOS 退役移至 Windows 2012R2 x64 後出現狀況。情境為使用者上傳CSV給網頁解析,其格式如下:
SN,Data
1,1234
2,AB12
3,9527
4,5978
Data欄位有可能是純數字或英數字,當Data為英數字有時會讀不到,而且是全部英數字都被忽過,數字部分正常,但有時在下方多加一筆英數字就又可全部讀到,頗為詭異。修改程式碼將讀取結果逐筆印出後有了較明顯線索:發現在「某種條件」下,從Data欄位讀入的英數字會全部變成空白,但數字部分正常。英數字資料的筆數、位置似乎會影響結果,但多次實驗仍未理出頭緒。
爬文查到保哥的一篇老文章提到 TypeGuessRows 機碼及 Jet Engine 由前 8 列資料猜測欄位型別的行為。換言之,是「欄位格式自動偵測」惹的禍,由於 Data 欄位混雜純數字與英數字,Jet 引擎自動偵測決定將其視為純數字或是英數字,若被判定成數字,其中的英數字資料就會被當成NULL,轉為字串後變成空字串。在德瑞克的另一篇文章,則有更詳細的情境範例描述:
如果您在同一欄中混合使用數值和文字值,會發生嚴重的問題。
Jet 和 ODBC 提供者都會傳回主要類型的資料,但是次要資料類型則會傳回 NULL (空) 值。
如果同一欄中兩種類型混合使用的比例相同,提供者會選擇數值而非文字值。
例如:
(1) 在 8 個已掃描的列中,如果欄位中包含 5 個數值和 3 文字值,提供者會傳回 5 個數值和 3 個 Null 值。
(2) 在 8 個已掃描的列中,如果欄位中包含 3 個數值和 5 文字值,提供者會傳回 3 個 Null 值和 5 個文字值。
(3) 在 8 個已掃描的列中,如果欄位中包含 4 個數值和 4 文字值,提供者會傳回 4 個數值和 4 個 Null 值。
但有個大疑點:我確認 Registry 中 TypeGuessRows 為 8,ImportMixedTypes 為 Text,實測就算前 8 筆都是英數字,欄位仍可能被判定為數字導致英數字空白,而這種混雜型別狀況依 ImportMixedTypes='Text' 明明要為文字才對。
最後,我才驚覺 Jet 解析 CSV 時使用的 Registry 與 Excel 有別,ImportMixedTypes 預設 Majority Type,而 MaxScanRows為 25,「為什麼前 8 筆全是英文仍會導致英數字變 NULL」有了完美解釋。
補充:Text Jet Engine Registry設定說明
又長了知識。
【2016-10-08更新】感謝網友張峰銘補充,若不想讓Jet Engine瞎猜一通,可使用Scehma.ini強制指定欄位型別(參考)。
【延伸閱讀】