考虑以下最小、完整且可验证的示例代码(请参阅此处的 dbfiddle):
CREATE TABLE [dbo].[test]
(
[i] bigint NOT NULL
identity(1,1)
PRIMARY KEY CLUSTERED
, [d] varchar(10) NOT NULL
);
GO
使用INSTEAD OF INSERT, UPDATE
触发器:
CREATE TRIGGER [dbo_test_trigger]
ON [dbo].[test]
INSTEAD OF INSERT, UPDATE
AS
BEGIN
IF ROWCOUNT_BIG() = 0 RETURN;
SET NOCOUNT ON;
MERGE INTO [dbo].[test] [target]
USING [inserted] [source] ON [target].[i] = [source].[i]
WHEN NOT MATCHED THEN
INSERT
(
[d]
)
VALUES
(
[source].[d]
)
WHEN MATCHED THEN
UPDATE
SET [target].[d] = [source].[d];
END;
GO
我正在对表进行插入,希望获得插入的标识值,但是返回的值是0
:
DECLARE @output TABLE
(
[i] bigint NOT NULL
, [d] varchar(10) NOT NULL
);
INSERT INTO [dbo].[test]
(
[d]
)
OUTPUT
[inserted].[i]
, [inserted].[d]
INTO @output
(
[i]
, [d]
)
VALUES ('test');
/* shows [i] is 0 */
SELECT *
FROM @output;
/* shows [i] is 1 */
SELECT *
FROM [dbo].[test];
结果是:
我 | d |
---|---|
0 | 测试 |
和
我 | d |
---|---|
1 | 测试 |
期望的结果是两组输出匹配,但事实并非如此。
我究竟做错了什么?
我已经看到了这一点,但是这似乎很不同,因为我根本没有使用视图。在我的示例中,触发器位于桌子上。
这种行为令人困惑并且记录很少。
它曾经在INSTEAD OF INSERT Triggers中有更好的记录(链接到 2008 R2 文档):
解释是,在捕获值时(触发器触发之前),SQL Server 无法知道触发器实际将插入多少行。它不会提前分配标识值,并在已知触发器结果时尝试以某种方式将它们匹配。
报价单:
传达一般概念。这并不意味着返回的结果在所有方面都与发生基础操作时完全相同。这在所有情况下都是不可能的。
上一段说(强调):
在操作发生之前不会分配任何标识值
INSTEAD OF
,因此它们不能出现在OUTPUT
集合中。记住初始操作(本示例中的 INSERT)是针对隐藏的临时表进行的,尽管在执行计划中被标记为目标表本身,这可能会有所帮助。有关 INSTEAD OF 触发器的更多信息,请参阅我的文章“有趣的事情” 。
它的行为正如记录的那样。
相关引用:OUTPUT 子句 (Transact-SQL)
更新的演示:
结果
由于临时表在嵌套作用域中可见,因此我可以看到第一个输出是外部输出(甚至在调用触发器之前)。
如果取消注释,
WHERE 1 = 0
您可以看到无论如何都插入了预期的值。