SQL Server 2016 不支持原生物化视图,所谓“物化标签”实为业务自建聚合表(如 user_summary),需通过触发器手动维护;其核心难点在于多行操作、前后值差异处理、并发竞态及幂等性保障,且无 CONCURRENTLY 刷新机制。

SQL Server 2016 没有原生物化视图(MATERIALIZED VIEW),所谓“物化标签”实际是业务中对聚合表、统计表或冗余字段的俗称——比如一张 user_summary 表存着每个用户的订单总数、最新登录时间等预计算结果。这类表不能靠 REFRESH MATERIALIZED VIEW 自动刷新(那是 PostgreSQL 的语法),必须手动维护。触发器是常用手段,但直接套用其他数据库思路会出错。
为什么不能照搬 PostgreSQL 的 CONCURRENTLY 刷新逻辑
SQL Server 不支持 REFRESH MATERIALIZED VIEW 语句,也没有 CONCURRENTLY 关键字。你写的任何触发器都只能操作普通表,且必须自己处理:
• 多行变更时的批量逻辑
• UPDATE 前后值差异判断
• DELETE 后如何反向扣减聚合值
• 并发写入时的竞态风险(如两个事务同时更新同一用户订单数)
强行在 SQL Server 触发器里写“刷新视图”类逻辑,轻则数据不一致,重则死锁或阻塞。
AFTER INSERT/UPDATE/DELETE 触发器必须用 inserted/deleted 表
SQL Server 触发器依赖两个内存临时表获取上下文:inserted 存新增或更新后数据,deleted 存删除前或更新前数据。它们不是物理表,不可索引,但可 JOIN、GROUP BY。
常见错误包括:
• 在 INSERT 触发器里查 deleted(它为空)
• 在 DELETE 触发器里查 inserted(它为空)
• 忽略多行场景:一个 UPDATE 可能影响 1000 行,inserted 和 deleted 各含 1000 行,必须用集合操作而非标量赋值
• 把 inserted.id 当单值用,导致只处理第一行
CREATE TRIGGER trg_update_user_order_count ON orders AFTER INSERT, UPDATE, DELETE AS BEGIN SET NOCOUNT ON; <p>-- 正确:用 MERGE 或分步 UPDATE 处理多行 MERGE user_summary AS tgt USING ( SELECT customer_id, COUNT(<em>) AS cnt FROM inserted GROUP BY customer_id UNION ALL SELECT customer_id, -COUNT(</em>) AS cnt FROM deleted GROUP BY customer_id ) AS src ON tgt.user_id = src.customer_id WHEN MATCHED THEN UPDATE SET order_count = order_count + src.cnt WHEN NOT MATCHED THEN INSERT (user_id, order_count) VALUES (src.customer_id, src.cnt); END;</p>
避免在触发器里调用存储过程或复杂函数
触发器运行在主 DML 事务内,一旦被调用的存储过程出错或耗时过长,整个事务会回滚或超时。更危险的是:
• 存储过程内部再改同一张表,可能引发嵌套触发器(默认开启,可用 sp_configure 'nested triggers', 0 关)
• 函数若含 SELECT ... INTO 或临时表,可能因执行计划缓存污染拖慢性能
• 调用链接服务器或外部 API(如 sp_OACreate)会导致事务无法提升为分布式事务,直接失败
真实项目中,90% 的触发器性能问题源于过度封装——把聚合逻辑写进触发器体,比调用外部过程更可控、更易调试。
唯一安全的“自动维护”前提是目标表有唯一约束且逻辑幂等
如果你的“物化标签”表(如 user_summary)没建 UNIQUE(user_id),上面那个 MERGE 示例会报错或插入重复行。而如果业务允许某用户多次下单但只计一次(比如去重统计),你还得加额外条件过滤。
更重要的是:触发器逻辑必须幂等。例如,两次相同的 INSERT 不应让 order_count 加两次。这要求你在设计时明确:
• 是累加型(每行 insert 就 +1)
• 还是快照型(每次触发都重算全量)
前者快但难保一致,后者准但慢——SQL Server 2016 没有 CONCURRENTLY,全量重算大概率锁表几秒,线上慎用。










