bulk insert是sql server中真正能跑满磁盘i/o的导入方式,因其由sql server进程直接读取文件流、不走网络协议栈且不逐行解析;但前提是数据文件路径在服务进程视角下可达、格式参数(如format='csv'、fieldterminator、rowterminator)配置准确、且服务账户具备对应文件系统权限。

BULK INSERT 是 SQL Server 中真正能跑满磁盘 I/O 的导入方式,前提是数据文件路径可达、格式对得上、权限配得准。它不走网络协议栈,不逐行解析,直接由 SQL Server 进程读取文件流——所以快,也所以容易卡在第一步就失败。
SQL Server 能否直接读取你的 CSV 文件路径
这是最常被忽略的前提。BULK INSERT 的 FROM 'data_file' 路径是**SQL Server 服务进程视角下的路径**,不是你本地 SSMS 或 Windows 用户看到的路径。
- 本地 SQL Server:支持本地绝对路径(如
'C:dataimport.csv'),也支持 UNC 路径(如'\fileservershareimport.csv'),但 SQL Server 服务账户必须有读取权限 - Azure SQL 数据库 / 托管实例:不支持本地或 UNC 路径,只能从 Azure Blob Storage 或 Data Lake Gen2 加载,且需配置
DATA SOURCE和 SAS 令牌或托管标识 - 路径中不能含通配符(
*或?),也不支持相对路径(如'./data.csv')
BULK INSERT 导入 CSV 必须处理的三个格式细节
CSV 不是标准格式,BULK INSERT 默认按 char 类型逐字节读取,遇到换行、引号、逗号就容易错位。必须显式声明:
-
FIELDTERMINATOR = ','—— 明确字段分隔符,别依赖默认(默认是) -
ROWTERMINATOR = ' '或'0x0a'—— Windows 换行是,Linux 是;若文件混用,优先用'0x0a'避免漏行 -
FORMAT = 'CSV'—— SQL Server 2017+ 支持该选项,会自动处理双引号包裹字段、转义双引号等规则;不加这个,就得自己写FIELDQUOTE = '"'+ 手动处理嵌套引号
示例语句:
BULK INSERT dbo.Customers FROM 'C:datacustomers.csv' WITH ( FORMAT = 'CSV', FIRSTROW = 2, -- 跳过表头 FIELDTERMINATOR = ',', ROWTERMINATOR = '0x0a', CODEPAGE = '65001' -- UTF-8 编码 );
常见错误:为什么 BULK INSERT 报 “操作系统错误 5” 或 “无法打开文件”
这不是 SQL 语法错,而是 Windows 权限/路径层问题:
-
操作系统错误 5:SQL Server 服务账户(如NT SERVICEMSSQLSERVER)没有对文件所在目录的“读取 & 执行”权限 —— 给目录加权限,不是给文件加 -
无法打开文件:路径存在但 SQL Server 进程根本看不到(比如你用 SSMS 连的是命名实例,但文件放在了另一台机器;或者用了映射网络驱动器Z:,而服务账户不识别用户级映射) - Azure SQL 报
Could not bulk insert. Bulk load data from a remote file is not supported:说明你还在用本地路径,必须改用DATA SOURCE+ Azure 存储 URL
性能与安全边界:别把 BULK INSERT 当万能锤
它快,但有硬限制和隐性成本:
- 不支持 Excel(
.xlsx)原生导入 —— 必须先导出为 CSV/TXT;也不支持 JSON/Parquet(FORMAT = 'PARQUET'在 SQL Server 中仍不支持) - 不校验约束、不触发触发器(除非显式加
FIRE_TRIGGERS和CHECK_CONSTRAINTS),但加了就明显变慢 - 事务日志增长剧烈:单次导入 100 万行可能产生数 GB 日志,建议在
bulk-logged或simple恢复模式下操作,并确认磁盘空间充足 - 目标表若有
IDENTITY列,默认跳过导入值;要保留原始 ID,必须加KEEPIDENTITY,且登录用户需有ADMINISTER BULK OPERATIONS权限
真正麻烦的从来不是语法,而是让 SQL Server 进程“看见”那个文件,并让它相信里面的数据结构和表定义能对上——这两步没走通,后面所有参数都白设。










