方案選擇指引
- 純大量插入:
SqlBulkCopy (BCP) 效能最佳
- 混合插入/更新/刪除:
SqlDataAdapter + UpdateBatchSize 效能優異
方案一:SqlBulkCopy 大量插入(含交易控制)
public static void BulkCopy(string connString, string tableName, DataTable dt)
{
using (SqlConnection conn = new SqlConnection(connString))
{
conn.Open();
using (SqlTransaction transaction = conn.BeginTransaction())
{
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction))
{
bulkCopy.BatchSize = 2000; // 每批 2000 筆
bulkCopy.BulkCopyTimeout = 300; // 逾時秒數
bulkCopy.DestinationTableName = tableName;
try
{
// 欄位對應:DataTable 欄位名 = 資料表欄位名
foreach (DataColumn col in dt.Columns)
{
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
bulkCopy.WriteToServer(dt);
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
}
}
}
}
關鍵參數說明
| 屬性 | 說明 | 建議值 |
BatchSize | 每批次筆數,減少記憶體壓力 | 1000-5000 |
BulkCopyTimeout | 逾時秒數(預設 30 秒) | 300+ |
ColumnMappings | 來源與目標欄位對應,順序不同時必設 | 逐一 Add |
SqlBulkCopyOptions | 選項:CheckConstraints, FireTriggers, KeepIdentity 等 | 視需求 |
方案二:SqlDataAdapter 批次更新(支援 Insert/Update/Delete)
public static void Update(string connString, DataTable table)
{
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand comm = conn.CreateCommand();
comm.CommandTimeout = 300;
comm.CommandType = CommandType.Text;
using (SqlDataAdapter adapter = new SqlDataAdapter(comm))
{
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(adapter);
commandBuilder.ConflictOption = ConflictOption.OverwriteChanges;
try
{
conn.Open();
adapter.UpdateBatchSize = 5000; // 批次更新筆數
// 開始交易
adapter.SelectCommand.Transaction = conn.BeginTransaction();
// 若 DataTable 有自訂 SelectCommand SQL
if (table.ExtendedProperties["SQL"] != null)
{
adapter.SelectCommand.CommandText = table.ExtendedProperties["SQL"].ToString();
}
adapter.Update(table); // 執行批次更新
// 提交交易
adapter.SelectCommand.Transaction.Commit();
}
catch (Exception ex)
{
if (adapter.SelectCommand != null && adapter.SelectCommand.Transaction != null)
{
adapter.SelectCommand.Transaction.Rollback();
}
throw;
}
}
}
}
關鍵技術點
- UpdateBatchSize:啟用批次更新,減少往返次數(預設 1,逐筆執行)
- SqlCommandBuilder:自動產生 INSERT/UPDATE/DELETE 指令,需資料表有主鍵
- ConflictOption.OverwriteChanges:衝突時以資料庫最新值覆蓋
- 交易控制:透過
SelectCommand.Transaction 統一管理
- ExtendedProperties["SQL"]:可攜帶自訂查詢 SQL 供 CommandBuilder 參考
效能比較與適用場景
| 指標 | SqlBulkCopy | SqlDataAdapter 批次更新 |
| 操作類型 | 僅 INSERT | INSERT / UPDATE / DELETE |
| 速度 | 極快(最接近 BCP 工具) | 快(批次模式下接近 BulkCopy) |
| 記憶體 | 分批處理,可控 | 需載入完整 DataTable |
| 觸發器 | 可選擇 FireTriggers | 逐批觸發 |
| 身分值保留 | 可選擇 KeepIdentity | 依資料表設定 |
| 適用場景 | 資料匯入、ETL、日誌大量寫入 | 離線編輯回寫、同步化、混合操作 |
實務建議
- 大量插入優先用
SqlBulkCopy,搭配交易確保一致性
- 需更新/刪除時用
SqlDataAdapter,設定 UpdateBatchSize > 1
- 資料量極大(百萬級)考慮分批次呼叫、暫存表、資料分割
- 生產環境務必測試批次大小對效能與記憶體的影響
- 避免在單一交易處理過多資料導致鎖定升級或記錄檔暴增