output子句必须紧随insert/update/delete语句末尾,不可独立使用或被分号、换行隔开;只能引用deleted/inserted伪表字段,不支持getdate()等副作用函数,多行结果应存入表变量避免混淆。

OUTPUT子句必须跟在INSERT/UPDATE/DELETE语句后面,不能独立使用
很多人写完 UPDATE 就直接加 OUTPUT,结果报错“Incorrect syntax near OUTPUT”。根本原因是 OUTPUT 不是单独语句,它必须紧贴在 DML 语句末尾(中间不能有分号、换行或其它语句隔开)。
常见错误写法:
UPDATE Users SET Status = 'Active' WHERE ID = 123;<br> OUTPUT inserted.ID, inserted.Email;
这会直接报错。正确写法是:
UPDATE Users SET Status = 'Active' WHERE ID = 123 OUTPUT inserted.ID, inserted.Email;
-
OUTPUT必须和 DML 在同一个批处理中,且紧跟其后 - 不能在
IF或BEGIN...END块里把 DML 和OUTPUT拆成两行写(除非用括号或显式批处理控制) - 如果要捕获多行结果,建议用表变量接收,而不是依赖客户端直接读取结果集(尤其在复杂存储过程中)
用表变量捕获OUTPUT结果,避免游标或多次查询
直接让 OUTPUT 返回结果集,在存储过程中容易和其它 SELECT 混淆,导致调用方读错结果集顺序。更稳妥的做法是把输出内容存进表变量,后续再处理。
示例:更新用户状态并记录变更前后的邮箱
DECLARE @Changed TABLE (ID INT, OldEmail NVARCHAR(100), NewEmail NVARCHAR(100));<br><br> UPDATE Users <br> SET Email = LOWER(Email) + '.updated'<br> OUTPUT deleted.ID, deleted.Email, inserted.Email<br> INTO @Changed (ID, OldEmail, NewEmail)<br> WHERE Status = 'Pending';
-
INTO @Changed是关键,它把输出定向到表变量,不产生额外结果集 - 只能用
deleted和inserted伪表字段,不能引用原表别名(如u.Email会报错) - 表变量结构必须和
OUTPUT列完全匹配(类型、顺序、数量) - 注意
deleted在INSERT中为空,inserted在DELETE中为空——UPDATE 两者都有
OUTPUT在MERGE中行为特殊,deleted/inserted含义取决于操作类型
MERGE 的 OUTPUT 伪表不是固定指“旧值”或“新值”,而是取决于当前执行的是 WHEN MATCHED 还是 WHEN NOT MATCHED BY TARGET 等分支。
例如:
MERGE Orders AS tgt<br> USING (SELECT 1001 AS OrderID, 'Shipped' AS Status) AS src ON tgt.OrderID = src.OrderID<br> WHEN MATCHED THEN UPDATE SET Status = src.Status<br> WHEN NOT MATCHED THEN INSERT (OrderID, Status) VALUES (src.OrderID, src.Status)<br> OUTPUT $action, inserted.OrderID, deleted.Status, inserted.Status;
-
$action字段必加,值为'INSERT'、'UPDATE'或'DELETE' - 在
UPDATE分支中,deleted.Status是更新前的值,inserted.Status是更新后的值 - 在
INSERT分支中,deleted.*全为 NULL;反之DELETE分支中inserted.*全为 NULL - 不要假设
deleted总有数据——运行时分支不同,NULL 值很常见
OUTPUT不支持某些函数和子查询,容易触发“Invalid use of a side-effecting operator”
试图在 OUTPUT 子句里调用 GETDATE()、NEWID()、ISNULL()(含列参数时)、或任何带副作用的函数,SQL Server 会直接报错。
错误示例:OUTPUT inserted.ID, GETDATE() AS UpdatedAt → 报错OUTPUT inserted.ID, ISNULL(inserted.Email, 'N/A') → 报错(ISNULL 被视为副作用操作)
- 只允许列引用、常量、简单表达式(如
inserted.ID + 1) - 需要计算逻辑(比如默认值、格式化时间),必须先在
UPDATE的SET部分赋值给变量或计算列,再从inserted引出 - 想记录操作时间?提前
DECLARE @Now DATETIME2 = GETDATE();,然后OUTPUT inserted.ID, @Now
真正难的不是语法,而是理解 deleted 和 inserted 是瞬时快照——它们只反映本次语句影响的那一组行,且不可二次查询。一旦没用 INTO 接住,或者没在语句末尾立刻用上,信息就永远消失了。











