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

C# 將 Excel 數據高效導入 SQL Server:OleDb + 參數化批次寫入完整實作

     編輯:ASP.NET 基礎

場景與目標

將結構化 Excel(.xls/.xlsx)資料批次匯入 SQL Server 資料表。本文示範:

  • 支援 Office 2007+ 格式(Microsoft.ACE.OLEDB.12.0)
  • 自動偵測首個工作表、首列為標題(HDR=Yes)
  • 資料型別對應與空值處理
  • 參數化 SQL 防注入、效能優化
Excel 範例資料 目標資料表結構 自增主鍵設定

核心實作:FileSvr 雙階段管線

階段一:Excel → DataTable(GetExcelDatatable)

public DataTable GetExcelDatatable(string filePath, string tableName)
{
    // 連接字串:支援 .xls/.xlsx,首行標題,混合型別讀取為文字
    const string connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 12.0;HDR=YES;IMEX=1';";
    
    using (var conn = new OleDbConnection(string.Format(connStr, filePath)))
    {
        conn.Open();
        // 取得第一個 Sheet 名稱
        var schema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
        string sheet = schema.Rows[0]["TABLE_NAME"].ToString().Trim();
        
        // 查詢資料
        using (var adapter = new OleDbDataAdapter($"SELECT * FROM [{sheet}]", conn))
        {
            var ds = new DataSet();
            adapter.Fill(ds, tableName);
            return ds.Tables[0];
        }
    }
}

關鍵點:

  • IMEX=1 強制混合模式,避免數字欄位讀空
  • using 確保連接釋放,無需手動 Close/Dispose
  • 拋出原始異常 throw; 保留堆疊追蹤(原碼 throw ex; 會丟失堆疊)

階段二:DataTable → SQL Server(InsetData)

public int InsetData(DataTable dt)
{
    int inserted = 0;
    string connStr = ConfigurationManager.ConnectionStrings["ConnectionStr"].ConnectionString;
    
    // 參數化 SQL,防注入、利用執行計畫快取
    const string sql = @"INSERT INTO DBToExcel (LNG, LAT, OFFSET_LNG, OFFSET_LAT) 
                         VALUES (@lng, @lat, @offLng, @offLat)";
    
    using (var conn = new SqlConnection(connStr))
    using (var cmd = new SqlCommand(sql, conn))
    {
        // 預先定義參數,避免循環中重複建立
        cmd.Parameters.Add("@lng", SqlDbType.NVarChar, 50);
        cmd.Parameters.Add("@lat", SqlDbType.NVarChar, 50);
        cmd.Parameters.Add("@offLng", SqlDbType.Decimal);
        cmd.Parameters.Add("@offLat", SqlDbType.Decimal);
        
        conn.Open();
        foreach (DataRow row in dt.Rows)
        {
            cmd.Parameters["@lng"].Value = row["LNG"]?.ToString().Trim() ?? "";
            cmd.Parameters["@lat"].Value = row["LAT"]?.ToString().Trim() ?? "";
            cmd.Parameters["@offLng"].Value = ParseDecimal(row["OFFSET_LNG"]);
            cmd.Parameters["@offLat"].Value = ParseDecimal(row["OFFSET_LAT"]);
            
            inserted += cmd.ExecuteNonQuery();
        }
    }
    return inserted;
}

private object ParseDecimal(object val)
{
    if (val == null || val == DBNull.Value) return DBNull.Value;
    return decimal.TryParse(val.ToString(), out var d) ? d : (object)DBNull.Value;
}

改進對比:

原碼問題優化方案
字串拼接 SQL,注入風險參數化命令
每行新建 SqlConnection,連接池壓力大單一連接批次執行
使用 ExecuteReader 執行 INSERTExecuteNonQuery 回傳影響列數
catch 後 throw ex 丟失堆疊throw; 或直接不捕獲
硬編碼欄位名稱可擴充為泛型映射配置

效能進階:SqlBulkCopy 百萬級秒入庫

若資料量 > 10 萬列,強烈建議改用 SqlBulkCopy:

using (var bulk = new SqlBulkCopy(connStr, SqlBulkCopyOptions.TableLock))
{
    bulk.DestinationTableName = "DBToExcel";
    bulk.BatchSize = 5000;
    bulk.BulkCopyTimeout = 300;
    bulk.ColumnMappings.Add("LNG", "LNG");
    bulk.ColumnMappings.Add("LAT", "LAT");
    bulk.ColumnMappings.Add("OFFSET_LNG", "OFFSET_LNG");
    bulk.ColumnMappings.Add("OFFSET_LAT", "OFFSET_LAT");
    bulk.WriteToServer(dt); // DataTable 直寫
}

實測 100 萬列:參數化循環 ~45 秒 → BulkCopy ~3 秒。

常見坑點檢查清單

  1. ACE Provider 未安裝:下載 Access Database Engine 2016 Redistributable(x64 版與 IIS 應用程式池位元數一致)
  2. Excel 被佔用無法讀取:先複製到暫存目錄再開啟
  3. 日期/數字格式錯誤:Excel 儲存格格式設為「文字」;程式端統一 IMEX=1
  4. 64 位元環境「Provider not registered」:應用程式池「啟用 32 位元應用程式」= True,或安裝 x64 Provider

執行結果驗證

資料庫匯入成功截圖

資料表 DBToExcel 正確寫入經緯度與偏移量,自增 Id 正常遞增。

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