INSERT INTO dbo.Students (StudentID, Name, School)
VALUES
('040501', '孫明', '山東大學'),
('040502', '李浩', '山東師範'),
('040503', '王剛', '煙台大學')
優點:單一陳述式、原子性、計畫快取友善、語法簡潔、最多 1000 列值。
INSERT INTO dbo.Students (StudentID, Name, School)
SELECT '040501', '孫明', '山東大學' UNION ALL
SELECT '040502', '李浩', '山東師範' UNION ALL
SELECT '040503', '王剛', '煙台大學'
缺點:冗長、每列獨立 SELECT、舊版優化器可能產生多計畫。
-- C# 批次參數化範例
const string sql = @"
INSERT INTO dbo.Students (StudentID, Name, School)
VALUES (@id, @name, @school)";
using (var cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.Add("@id", SqlDbType.VarChar, 20);
cmd.Parameters.Add("@name", SqlDbType.NVarChar, 50);
cmd.Parameters.Add("@school", SqlDbType.NVarChar, 100);
foreach (var batch in students.Chunk(1000))
{
// Table-Valued Parameter (TVP) 更高效
// 或迴圈加參數執行
}
}
-- 1. 建立使用者定義表類型
CREATE TYPE dbo.StudentList AS TABLE (
StudentID VARCHAR(20),
Name NVARCHAR(50),
School NVARCHAR(100)
)
-- 2. 儲存程序接收 TVP
CREATE PROCEDURE dbo.BulkInsertStudents @Students dbo.StudentList READONLY
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.Students (StudentID, Name, School)
SELECT StudentID, Name, School FROM @Students
END
-- 3. C# 呼叫
var dt = new DataTable();
dt.Columns.Add("StudentID", typeof(string));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("School", typeof(string));
// 填入資料...
using (var cmd = new SqlCommand("dbo.BulkInsertStudents", conn) { CommandType = CommandType.StoredProcedure })
{
var param = cmd.Parameters.AddWithValue("@Students", dt);
param.SqlDbType = SqlDbType.Structured;
param.TypeName = "dbo.StudentList";
cmd.ExecuteNonQuery();
}
優點:單次往返、伺服器端集合操作、型別安全、計畫重用、無 1000 列限制。
| 方法 | 耗時 | 網路往返 | 計畫快取 | 適用場景 |
|---|---|---|---|---|
| 單列迴圈 INSERT | ~45 秒 | 10,000 | 單一參數化計畫 | 極小量、邏輯複雜 |
| VALUES 列建構子 (1000 列/批) | ~1.2 秒 | 10 | 單一計畫 | 中量、臨時腳本 |
| UNION ALL (1000 列/批) | ~1.5 秒 | 10 | 可能多計畫 | 舊版相容 |
| TVP (單次) | ~0.3 秒 | 1 | 單一計畫 | 大量、生產系統 |
| SqlBulkCopy | ~0.2 秒 | 1 (流式) | N/A | 極大量、ETL、資料遷移 |
using (var bulk = new SqlBulkCopy(conn))
{
bulk.DestinationTableName = "dbo.Students";
bulk.BatchSize = 5000;
bulk.BulkCopyTimeout = 300;
bulk.ColumnMappings.Add("StudentID", "StudentID");
bulk.ColumnMappings.Add("Name", "Name");
bulk.ColumnMappings.Add("School", "School");
bulk.WriteToServer(dataTable); // 或 IDataReader
}
VALUES (...), (...), ...TVP 或分批 VALUESSqlBulkCopy + TABLOCK + 簡單復原模式UNION ALL 分批VALUES 超過 1000 列 → 錯誤 10738DISABLE INDEX 後重建)