SQL Server经ODBC链接服务器插入PostgreSQL报无法识别插入行错误
报错核心是SQL Server通过MSDASQL OLEDB提供程序调用PostgreSQL ODBC驱动执行写操作时,驱动无法为写入目标生成唯一行标识,无法完成写入后的行确认流程。SELECT操作不需要行级定位校验,因此可以正常运行。你当前OpenQuery写入目标为视图(名称带_v1后缀),是触发该问题的最常见诱因。
方案1:调整链接服务器配置,直接写入物理表
不要将视图作为OpenQuery的写入目标,替换为视图对应的底层物理表,先执行以下命令配置链接服务器参数:
-- 开启高级配置选项 EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'Ad Hoc Distributed Queries', 1; RECONFIGURE; -- 关闭分布式事务升级,开启非事务写入 EXEC master.dbo.sp_serveroption @server = N'DDL_PUYAML1_64', @optname = N'remote proc transaction promotion', @optvalue = N'false'; EXEC master.dbo.sp_serveroption @server = N'DDL_PUYAML1_64', @optname = N'data access', @optvalue = N'true'; EXEC master.dbo.sp_serveroption @server = N'DDL_PUYAML1_64', @optname = N'non transacted updates', @optvalue = N'true';
配置完成后通过四部分名称直接写入物理表,替换代码中的schema和表名为PG端实际值:
INSERT INTO [DDL_PUYAML1_64].[ws_sls_core].[实际schema名].[实际物理表名] (sn1, encl, encl_model) SELECT sn1,encl,encl_model FROM #temp1
注意:PostgreSQL默认schema通常为public,不要直接对视图执行分布式写入,绝大多数版本的PostgreSQL ODBC驱动无法为视图自动生成唯一行标识。
方案2:通过OPENQUERY执行原生PG写入语句,绕过MSDASQL行校验
放弃INSERT INTO OPENQUERY(...) SELECT ...的写法,直接把INSERT逻辑放到远端执行,完全在PG端完成写入操作,不会触发MSDASQL的行识别校验。小数据量可以直接拼接SQL执行:
DECLARE @insertSql NVARCHAR(MAX); SELECT @insertSql = STRING_AGG( N'INSERT INTO ws_sls_core.dd_enclosures_forarsdashboard_v1(sn1,encl,encl_model) VALUES (' + '''' + REPLACE(CAST(sn1 AS NVARCHAR(MAX)),'''','''''') + ''',' + '''' + REPLACE(CAST(encl AS NVARCHAR(MAX)),'''','''''') + ''',' + '''' + REPLACE(CAST(encl_model AS NVARCHAR(MAX)),'''','''''') + '''' +');', N'' ) FROM #temp1; EXEC (@insertSql) AT [DDL_PUYAML1_64];
如果数据量较大,可以拆成每1000行一批执行,避免单条SQL过长。如果PG端的视图配置了INSTEAD OF触发器,这个写法可以正常写入视图,不需要操作底层物理表。
方案3:调整PostgreSQL ODBC驱动配置
打开Windows系统的64位ODBC数据源管理器,找到链接服务器使用的PostgreSQL DSN,进入配置页修改以下参数:
- 勾选 True is -1 选项
- 取消勾选 Use Declare/Fetch 选项
- 勾选 Updatable Cursors 选项
- 将 Level of rollback on errors 设置为 Transaction
保存配置后重启SQL Server服务,再测试写入操作。
方案4:替换桥接驱动
MSDASQL是ODBC转OLEDB的桥接层,本身对PostgreSQL写操作兼容性很差,可以替换为PostgreSQL原生OLEDB提供程序,配置链接服务器时直接选择原生PG OLEDB驱动,不走ODBC桥接,从根源上避免这类行识别报错。
- 分布式写入PG时不要开启MS DTC分布式事务,PG ODBC驱动对DTC兼容性极差,极易触发校验失败
- 写入字段的数据类型必须和PG端定义完全匹配,字符串、整数、时间类型的隐式转换问题也可能偶发同类报错
- 不要在OpenQuery的SELECT子句中使用不带主键/唯一约束的表或视图,驱动无法生成稳定的行定位标识
内容的提问来源于stack exchange,提问作者kashif ashraf

