BULK INSERT 不支持直接导入 geography/geometry 字段,因二者为 CLR UDT,无批量导入协议;必须先以 varchar 列暂存 WKT 字符串,再通过 UPDATE 批量调用 STGeomFromText 转换。

BULK INSERT 本身不支持直接导入 geography 或 geometry 类型字段——这是最常被忽略的硬限制。你不能把 WKT 字符串(如 'POINT(120.1 30.2)')直接扔进 BULK INSERT 就指望它自动转成空间类型,SQL Server 会报错:Cannot convert a char value to a geography value。
必须先用字符串列接收原始数据,再通过 UPDATE 或计算列转换。绕不开这一步。
为什么 BULK INSERT 不能直接写入 geography 列
BULK INSERT 是按本机格式或字符格式解析文件的,而 geography 是 CLR UDT(用户定义类型),没有对应的二进制序列化协议暴露给批量导入引擎。SQL Server 的批量路径只认基础类型(int、varchar、datetime2 等)和少数本机扩展类型(如 xml),geography 不在其中。
常见错误现象:
- 导入时出现
Conversion failed when converting character string to geography - 目标列为
geography,但数据文件里写了POINT(120.1 30.2),BULK INSERT直接报类型不匹配 - 用
FORMAT = 'CSV'也无效——格式选项不改变类型校验逻辑
可行方案:两阶段导入(字符串暂存 + 批量转换)
核心思路是拆开“数据搬运”和“类型转换”两个动作,避免在 BULK INSERT 过程中触发逐行 CAST。
实操建议:
- 建表时,为每个空间字段额外加一个
varchar(max)暂存列,例如geom_wkt - 用
BULK INSERT把 WKT 字符串原样导入到geom_wkt,不碰geography列 - 导入完成后,用单条
UPDATE批量转换:UPDATE dbo.Points SET geom = geography::STGeomFromText(geom_wkt, 4326) WHERE geom_wkt IS NOT NULL;
- 加上
TABLOCK和MAXDOP 1可减少日志竞争,尤其在大表上
SqlBulkCopy 能否绕过这个限制?
不能。虽然 SqlBulkCopy 支持 DataTable 中的 SqlDbType.Udt,但 .NET 的 Microsoft.SqlServer.Types 库要求你提前把 WKT 解析成 SqlGeography 实例——这意味着你得在内存里逐行调用 SqlGeography.STGeomFromText(),彻底失去批量优势,还吃光内存。
更现实的做法是:
- 保持源数据为 WKT 字符串,走
BULK INSERT到暂存列 - 或改用
bcp命令行工具 + 格式文件,同样只能导到字符串列 - 如果必须用
SqlBulkCopy,就用SqlDbType.Variant或SqlDbType.NVarChar接收,后续再批量转换
容易被忽略的性能陷阱
即使走两阶段,仍有几个关键点直接影响最终耗时:
-
geography::STGeomFromText()在UPDATE中是非 SARGable 的,务必在geom_wkt上建筛选索引(如WHERE geom_wkt IS NOT NULL)或用WHERE geom IS NULL控制范围 - 别在
UPDATE时开事务日志全量记录——对千万级数据,考虑启用简单恢复模式或用SELECT INTO构建新表再切换 -
4326是常用 SRID,但如果数据全是同一坐标系,显式传参比用默认值略快;若混用不同 SRID,必须在 WKT 里带SRID=4326;前缀,否则STGeomFromText会默认设为 0


















