SQL Server中高效批量插入需用BULK INSERT或SqlBulkCopy,配合TABLOCK、临时切换为BULK_LOGGED恢复模式、禁用约束与触发器、预排序数据、禁用非聚集索引,并显式设置字段/行终止符等,以减少日志、锁争用和I/O开销。

直接用 BULK INSERT 导入超大 CSV 文件,关键不是“能不能快”,而是“不加控制就一定会卡住或失败”。SQL Server 默认行为对大文件极不友好,必须显式关闭约束、锁定表、跳过日志开销,否则导入中途可能因日志爆满、锁升级或内存耗尽而中断。
必须禁用的默认行为:CHECK 约束、触发器与逐行日志
默认情况下,BULK INSERT 会逐行校验 CHECK 约束、执行触发器,并写完整事务日志——这对 GB 级 CSV 是灾难性的。实际导入前要主动绕过这些:
-
IGNORE_CONSTRAINTS和IGNORE_TRIGGERS只在OPENROWSET(BULK)中作为表提示生效,BULK INSERT不支持;必须改用CHECK_CONSTRAINTS = OFF和FIRE_TRIGGERS = OFF(注意:这是 WITH 子句参数,不是表提示) -
TABLOCK强制表级锁,避免锁升级导致阻塞,且启用最小日志记录(前提是恢复模式为SIMPLE或BULK_LOGGED) - 确保数据库恢复模式不是
FULL,否则日志会暴涨;临时切到BULK_LOGGED是常见做法,但导入后需立即做日志备份
CSV 格式参数必须显式指定,不能依赖 FORMAT='CSV'
FORMAT = 'CSV' 看似方便,但它只在 Azure SQL Database / Azure SQL 托管实例中完全支持,在本地 SQL Server 2019 及更早版本中不识别该选项,会报错 Incorrect syntax near 'CSV'。稳妥做法是手动声明分隔符和引号:
使用 qbo-mileage CLI 及用户凭证,从 Airtable、Outlook 或 Google Calendar 记录生成 QuickBooks Online 里程 CSV 文件。
- 用
FIELDTERMINATOR = ','明确字段分隔符 - 用
FIELDQUOTE = '"'处理带逗号或换行的字段(注意:SQL Server 2017+ 才支持FIELDQUOTE) - 用
ROWTERMINATOR = '0x0a'(Linux 换行)或'\n'(Windows 下推荐用'0x0d0a')避免首行/末行解析错位 - 若 CSV 含 BOM,必须加
CODEPAGE = '65001',否则中文字段开头可能出现乱码字符
超大文件要分批,但 ROWS_PER_BATCH 不等于“每批提交”
ROWS_PER_BATCH 只影响统计估算和内存分配,**不会自动分事务提交**。它不改变单条 BULK INSERT 语句的原子性——失败仍需全量回滚。真正可控的分批导入需结合外部脚本(如 PowerShell + sqlcmd),按行号切片调用多次 BULK INSERT:
- 先用
SELECT COUNT(*) FROM OPENROWSET(BULK 'file.csv', ...)预估总行数(仅限小样本) - 用
FIRSTROW和LASTROW参数分段,例如每次处理 50 万行:FIRSTROW = 1,LASTROW = 500000;下一批设为FIRSTROW = 500001 - 注意:CSV 首行是标题时,
FIRSTROW必须从 2 开始,否则标题会被当数据插入 - 分批后务必在每批次之间加
CHECKPOINT或手动BACKUP LOG,防止日志无限制增长
错误处理不能只靠 ERRORFILE,得提前验证文件结构
ERRORFILE 只捕获格式错误行(如列数不匹配),但对类型转换失败(如把文本 'abc' 插入 INT 列)默认直接报错中断。更危险的是:某些隐式转换(如空字符串转 DATETIME)会产生非预期值而非报错。
- 导入前用
SELECT TOP 10 * FROM OPENROWSET(BULK 'file.csv', ...)抽样检查字段类型是否对齐 - 目标表所有非空列,CSV 对应列不能全为空;否则
KEEPNULLS不生效,会填默认值甚至插入失败 -
MAXERRORS设太大会掩盖真实问题,建议初试设为10,确认稳定后再调高 - 如果文件来自外部系统,优先让对方提供
.fmt格式文件,比硬编码分隔符更可靠
最常被忽略的一点:Azure Blob Storage 场景下,FROM 路径必须走 DATA SOURCE,不能直接写 SAS URL;本地文件路径则必须是 SQL Server 进程有权限访问的路径(不是你登录用户的路径),哪怕只是 UNC 路径,也得确认服务账户有读取权限。

















