output子句必须紧跟在update的set或from之后、where之前,且只能引用inserted/deleted伪表;可存入表变量避免阻塞,多表更新需用cte绕过限制。

OUTPUT子句必须紧跟在UPDATE之后,不能加WHERE或ORDER BY
SQL Server 的 OUTPUT 子句不是独立语句,而是 UPDATE 语法的一部分,位置非常严格:它必须直接跟在 SET 子句后面(或 FROM 子句之后,如果用了),不能插在 WHERE 前,也不能放在末尾再加 ORDER BY。写错位置会直接报错 Incorrect syntax near 'OUTPUT'。
常见错误写法:UPDATE ... SET ... WHERE ... OUTPUT inserted.id, deleted.name; —— 这里 OUTPUT 在 WHERE 后,非法。
正确顺序示例:
UPDATE Users SET status = 'archived' OUTPUT deleted.id, deleted.email, inserted.status WHERE last_login <h3>用INSERTED和DELETED伪表区分新旧值,别混淆字段来源</h3><p><code>INSERTED</code> 和 <code>DELETED</code> 是两个只读的内存伪表,分别代表更新后和更新前的行快照。对 <code>UPDATE</code> 来说,它们行数一致、结构相同,但字段值不同。容易踩的坑是:误以为 <code>INSERTED</code> 包含所有列(其实只含被修改列?错——它包含整行,未改的列值也照搬);或者在 <code>OUTPUT</code> 中漏写前缀,导致列名歧义(如 <code>id</code> 同时存在于两张伪表)。</p>
- 必须为每个输出字段显式指定
INSERTED.或DELETED.前缀,除非该字段在两张表中都不存在重名 - 想捕获“变化量”,可以写表达式:
INSERTED.balance - DELETED.balance AS delta - 不能在
OUTPUT中引用原表别名(如U.id),只能用INSERTED/DELETED
把OUTPUT结果插入临时表或表变量,避免仅SELECT导致阻塞
如果只写 OUTPUT ... SELECT ...,结果会直接返回给客户端,这在高并发 UPDATE 场景下可能引发锁等待延长——因为 SQL Server 需维持事务上下文直到结果集完全消费。更稳妥的做法是把 OUTPUT 结果导出到 @table_variable 或 #temp_table,后续再处理。
示例(存入表变量):
DECLARE @updated TABLE (id INT, old_email VARCHAR(100), new_status VARCHAR(20));<br><br>UPDATE Users<br>SET status = 'inactive'<br>OUTPUT deleted.id, deleted.email, inserted.status<br>INTO @updated<br>WHERE id IN (101, 102, 103);<br><br>SELECT * FROM @updated;
注意:INTO 子句要求目标表结构与 OUTPUT 列严格匹配(顺序、类型、空性),否则报错 The column name or number of supplied values does not match table definition。
OUTPUT不支持JOIN子句,多表更新需用CTE或子查询绕过
直接写 UPDATE t1 JOIN t2 ON ... OUTPUT ... 会失败——T-SQL 不允许在 UPDATE ... FROM ... JOIN 语法中使用 OUTPUT 引用被 JOIN 的表字段(比如 t2.name)。根本原因是 OUTPUT 只能访问 INSERTED/DELETED,而 JOIN 表不在其中。
可行解法是用 CTE 预先定位目标行,再 UPDATE CTE:
WITH target AS (<br> SELECT u.id, u.email, o.order_date<br> FROM Users u<br> INNER JOIN Orders o ON u.id = o.user_id<br> WHERE o.order_date )<br>UPDATE target<br>SET email = 'migrated_' + email<br>OUTPUT inserted.id, deleted.email, inserted.email;
这里 target 是可更新的 CTE,OUTPUT 仍只通过 INSERTED/DELETED 暴露字段,但逻辑上已关联了多表数据。
真正难处理的是需要输出 JOIN 表中非主键字段的场景——那只能分两步:先查出要更新的 ID 列表,再用这些 ID 单独查关联数据,无法靠单条语句完成。











