使用 `OPENROWSET` 搭配 `Microsoft.Jet.OLEDB.4.0` (舊版 .xls) 或 `Microsoft.ACE.OLEDB.12.0` (.xlsx) 直接查詢 Excel 如資料表,無需 SSIS/導入精靈,適合臨時/輕量級 ETL。
-- .xls (Excel 97-2003)
SELECT * FROM OPENROWSET(
'Microsoft.Jet.OLEDB.4.0',
'Excel 8.0;HDR=YES;IMEX=1;Database=\\Server\Share\File.xls',
'SELECT * FROM [Sheet1$]'
);
-- .xlsx (Excel 2007+) - 需安裝 ACE Driver
SELECT * FROM OPENROWSET(
'Microsoft.ACE.OLEDB.12.0',
'Excel 12.0 Xml;HDR=YES;IMEX=1;Database=\\Server\Share\File.xlsx',
'SELECT * FROM [Sheet1$]'
);
關鍵屬性:
根因:OLEDB 掃描前 8 行 (預設 `TypeGuessRows=8`) 推斷型別。若前 8 行純數字,後面出現文字 → 文字讀為 NULL;若長數字 (身分證/卡號/手機) → 變科學記號/截斷。
解決三板斧:
將 `OPENROWSET` 查詢結果 `INSERT INTO` 目標表時,建議在 `SELECT` 列表中**顯式轉換**而非依賴隱含轉換。
INSERT INTO dbo.TargetTable (CarNo, PuDate, GuidStr, Amount, DrNo, ...)
SELECT
CONVERT(CHAR(4), car_no), -- 定長字串
CONVERT(DATETIME, [pu_date]), -- 日期
CAST(CAST([guid_col] AS DECIMAL(38,0)) AS VARCHAR(50)), -- 長數字/科學記號 -> 精確字串 (關鍵技巧)
CONVERT(DECIMAL(12,2), [amount_col]), -- 金額
CONVERT(CHAR(4), dr_no),
CAST(CAST([bigint_col] AS DECIMAL(38,0)) AS VARCHAR(50)), -- 大整數避免溢位
CAST(CAST([other_num] AS DECIMAL(38,0)) AS VARCHAR(50)),
CONVERT(DATETIME, [other_date]),
ISNULL([remark_col], '')
FROM OPENROWSET(...);
轉換邏輯解析:
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| OPENROWSET / OPENDATASOURCE | T-SQL 原生、即時查詢、無 ETL 工具依賴 | Provider 相依性、型別推斷脆弱、難處理超大檔、安全性風險 (Ad Hoc Distributed Queries) | 臨時分析、小檔 (<50MB)、DBA 手工操作 |
| SSIS (Import/Export Wizard / Package) | 視覺化、型別映射精確、錯誤流導向、記錄/檢查點、部署管理 | 學習曲線、部署相對重 | 定期/生產級 ETL、複雜轉換、大檔案 |
| BCP / BULK INSERT (CSV 中轉) | 極速、最小記錄、無 Provider 相依 | 需先將 Excel 另存 CSV (人工/腳本) | 大量匯入、資料倉儲建模、夜間批次 |
| 應用層 (C# EPPlus/ClosedXML + SqlBulkCopy / TVP) | 完全掌控解析邏輯、型別精確、單元測試友善、無 Server 端元件 | 需開發維護 | 企業級系統集成、使用者上傳匯入、複雜驗證規則 |