首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

C# 讀取 Excel 完整實戰:OLEDB 連線、資料型別陷阱與進階查詢技巧

     編輯:C# 入門

為什麼選擇 OLEDB 讀取 Excel?

在無法安裝 Office(伺服器環境)或需高效能批次讀取時,Microsoft.Jet.OLEDB.4.0 / Microsoft.ACE.OLEDB.12.0 提供輕量、無相依性的解決方案。將 Excel 檔案視為資料來源,以 SQL 語法查詢工作表,程式碼簡潔且效能穩定。

環境準備與連線字串詳解

1. 提供者選擇

Excel 版本提供者備註
.xls (Excel 97-2003)Microsoft.Jet.OLEDB.4.0僅 32 位元,需啟用 IIS 應用程式集區「啟用 32 位元應用程式」
.xlsx/.xlsb/.xlsm (Excel 2007+)Microsoft.ACE.OLEDB.12.0 或更新版支援 64 位元,需安裝 Access Database Engine

2. 連線字串關鍵屬性

// 基礎範本 (.xls)
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\test.xls;Extended Properties="Excel 8.0;HDR=YES;IMEX=1"

// 進階範本 (.xlsx)
Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\test.xlsx;Extended Properties="Excel 12.0 Xml;HDR=YES;IMEX=1"
屬性值說明
HDRYES (預設) / NOYES:第一列為欄位名;NO:第一列為資料,欄位名自動產生 F1、F2...
IMEX0 (預設) / 11 = 混合模式,將所有欄位視為文字讀取,解決「數字/日期欄位首列為文字導致讀成 null」問題
TypeGuessRows0~16 (預設 8)掃描前 N 列推斷資料型別,設 0 可掃描全表(較慢但準確)
ReadOnlyTrue / False唯讀模式可避免獨佔鎖定檔案

核心讀取程式碼(含資源釋放最佳實務)

public DataTable ReadExcelSheet(string filePath, string sheetName, bool hasHeader = true)
{
    string provider = filePath.EndsWith(".xlsx") 
        ? "Microsoft.ACE.OLEDB.12.0" 
        : "Microsoft.Jet.OLEDB.4.0";
    
    string extProps = $"Excel {(filePath.EndsWith(".xlsx") ? "12.0 Xml" : "8.0")};HDR={(hasHeader ? "YES" : "NO")};IMEX=1;ReadOnly=True";
    string connStr = $"Provider={provider};Data Source={filePath};Extended Properties=\"{extProps}\"";

    using (var conn = new OleDbConnection(connStr))
    using (var cmd = new OleDbCommand($"SELECT * FROM [{sheetName}$]", conn))
    using (var adapter = new OleDbDataAdapter(cmd))
    {
        var dt = new DataTable();
        conn.Open();
        adapter.Fill(dt);
        return dt;
    }
}

實戰場景與解決方案

1. 不知道工作表名稱?動態取得所有工作表

public List GetSheetNames(string filePath)
{
    var sheets = new List();
    string provider = filePath.EndsWith(".xlsx") ? "Microsoft.ACE.OLEDB.12.0" : "Microsoft.Jet.OLEDB.4.0";
    string connStr = $"Provider={provider};Data Source={filePath};Extended Properties=\"Excel 8.0;HDR=YES\"";

    using (var conn = new OleDbConnection(connStr))
    {
        conn.Open();
        // 取得結構描述:TABLE 類型即為工作表
        DataTable schema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
        foreach (DataRow row in schema.Rows)
        {
            string sheetName = row["TABLE_NAME"].ToString().TrimEnd('$'); // 移除尾端 $
            if (!sheetName.EndsWith("_FilterDatabase")) // 排除 Excel 內建篩選表
                sheets.Add(sheetName);
        }
    }
    return sheets;
}

2. 只需特定欄位或範圍?SQL 查詢語法

// 指定欄位:SELECT [欄位1], [欄位2] FROM [Sheet1$]
// 指定範圍:SELECT * FROM [Sheet1$A1:D100]  (A1:D100 為儲存格範圍)
// 指定整欄:SELECT * FROM [Sheet1$A:D]      (A 到 D 欄)
// 別名支援:SELECT [姓名] AS Name, [年齡] AS Age FROM [Sheet1$]

3. 常見資料型別陷阱與對策

現象原因解決方案
首列文字資料變成 nullOLEDB 掃描前 8 列發現多為數字/日期,推斷該欄為數值型,文字無法轉換變 null加上 IMEX=1 強制混合模式(全視為文字)
日期變成數字 (如 39902)Excel 內部以序數儲存日期 (1900/1/1 = 1),IMEX=1 讀出原始序數讀取後用 DateTime.FromOADate(doubleVal) 轉換,或改用 EPPlus/ClosedXML 保留格式
長數字 (身分證/信用卡) 變科學記號或截斷數值型別精度限制IMEX=1 讀為字串,或在 Excel 該欄設為「文字」格式並在前加單引號
空白列被讀入Excel 已使用範圍超過實際資料範圍讀取後以 DataTable.Select 或 LINQ 過濾全空列

進階:大檔案分批讀取避免記憶體暴衝

public IEnumerable ReadExcelInBatches(string filePath, string sheetName, int batchSize = 10000)
{
    string provider = filePath.EndsWith(".xlsx") ? "Microsoft.ACE.OLEDB.12.0" : "Microsoft.Jet.OLEDB.4.0";
    string connStr = $"Provider={provider};Data Source={filePath};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1;ReadOnly=True\"";

    using (var conn = new OleDbConnection(connStr))
    {
        conn.Open();
        // 先取得總筆數
        using (var countCmd = new OleDbCommand($"SELECT COUNT(*) FROM [{sheetName}$]", conn))
        {
            int total = (int)countCmd.ExecuteScalar();
            for (int offset = 0; offset < total; offset += batchSize)
            {
                string sql = $"SELECT * FROM [{sheetName}$] LIMIT {batchSize} OFFSET {offset}"; // ACE 支援 LIMIT/OFFSET
                // Jet 不支援 LIMIT,需用 TOP + WHERE 主鍵 > 上一批最大值 等替代方案
                using (var adapter = new OleDbDataAdapter(sql, conn))
                {
                    var dt = new DataTable();
                    adapter.Fill(dt);
                    yield return dt;
                }
            }
        }
    }
}

替代方案快速比較

方案優點缺點適用場景
OLEDB (本文)無 Office 相依、SQL 查詢彈性、效能佳型別推斷麻煩、需安裝 ACE、.xls 僅 32 位元伺服器端批次匯入、大量資料讀取
EPPlus / ClosedXML強型別、保留格式/樣式、支援 .xlsx 全功能記憶體佔用大、大檔案較慢需處理樣式、公式、圖表、中小檔案
NPOI支援 .xls/.xlsx、無相依、串流式讀寫 (SXSSF)API 較底層、程式碼較冗長跨平台、大檔案串流處理、Linux Docker 環境
ExcelDataReader輕量、快速、支援 .xls/.xlsx、無相依唯讀、不支援寫入純讀取、高效能匯入

總結檢核清單

  • [ ] 根據副檔名選對 Provider (Jet vs ACE)
  • [ ] 連線字串加上 IMEX=1;ReadOnly=True
  • [ ] 使用 using 確保連線/命令/配接器正確釋放
  • [ ] 動態取得工作表名稱避免硬編碼
  • [ ] 針對長數字、日期、混合型別欄位做後續轉換驗證
  • [ ] 大檔案採用分批讀取或改用串流式庫

掌握 OLEDB 讀取 Excel 的核心參數與陷阱,能讓您在企業級匯入匯出功能中游刃有餘。根據專案需求靈活選擇最適合的庫,才是長久之計。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved