能实现,但不推荐作为生产环境的主力同步方案——它容易卡住事务、跨服务器时依赖分布式事务或链接服务器,且批量操作处理不直观;sql server触发器本身不能直接写远程数据库,需通过openrowset或链接服务器中转,前者性能差且有安全风险,后者需配置rpc out和msdtc;触发器内须用组合判断区分insert/update/delete,批量操作须避免单行赋值错误;常见坑包括未加set nocount on、未设set xact_abort on、忽略主键冲突及无法捕获truncate操作;真正实时同步应优先选用发布/订阅或cdc+自定义消费者。

能实现,但不推荐作为生产环境的主力同步方案——它容易卡住事务、跨服务器时依赖分布式事务或链接服务器,且批量操作处理不直观。
触发器同步必须先解决跨库/跨服务器访问问题
SQL Server 触发器本身不能直接写远程数据库,得靠 OPENROWSET 或链接服务器(sp_addlinkedserver)中转。前者每次都要传连接字符串,性能差、密码明文风险高;后者需提前配置账号映射,且远程服务器必须启用 RPC Out 和 MSDTC(尤其跨机器时)。
- 用
OPENROWSET的写法像这样:INSERT INTO OPENROWSET('SQLNCLI', 'Server=192.168.1.101;Trusted_Connection=yes;', 'SELECT name,age FROM db.dbo.student'),但每次执行都重建连接,高并发下易超时 - 链接服务器方式更稳定,但配置命令里
sp_addlinkedsrvlogin的第三个参数若设为null,表示所有本地登录用户共享同一套远程凭据,权限模型变松散 - 如果目标表在另一台 SQL Server 上,而你没开 MSDTC 服务,
BEGIN DISTRIBUTED TRANSACTION会直接报错Msg 7391
触发器里怎么区分 insert/update/delete 并分别处理
不能只靠 IF EXISTS(SELECT * FROM inserted) 简单判断——因为 UPDATE 时 inserted 和 deleted 都非空,DELETE 时只有 deleted 有数据。得组合判断:
-
IF EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted)→ 是INSERT -
IF EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted)→ 是UPDATE -
IF NOT EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted)→ 是DELETE
注意:批量操作(如一次 UPDATE 1000 行)会让 inserted 和 deleted 各含 1000 行,不能用 SELECT @var = col FROM inserted 直接赋值(会取不确定行),必须用游标或集合操作(如 MERGE)来逐行或整集同步。
同步逻辑写在触发器里,最容易踩的三个坑
实际部署时,以下三点常被忽略,导致同步失败或死锁:
- 没加
SET NOCOUNT ON:触发器内每条INSERT/UPDATE都返回“X 行受影响”,可能让上层应用误判结果集异常而中断 - 没设
SET XACT_ABORT ON:远程操作失败时,本地事务不会自动回滚,造成主表已改、副表未同步的脏状态 - 没处理主键冲突或约束失败:比如目标表有唯一索引,而源表重复插入同名记录,触发器会直接报错中断,且不提供重试机制
另外,触发器无法捕获 TRUNCATE TABLE 操作(它不走触发器),如果业务中有清空表逻辑,这部分数据就彻底不同步了。
真正需要实时同步时,优先考虑发布/订阅或 CDC + 自定义消费者;触发器只适合结构极简单、QPS 极低、且能接受一定维护成本的场景。










