如何在使用而不是触发器时插入最后一个标识行

meh*_*tfi 8 trigger sql-server-2008 sql-server t-sql identity

当我插入表使用INSTEAD OF触发器,@@IdentityIDENT_CURRENT('Table')SCOPE_IDENTITY()返回null。如何获取插入行的最后一个标识?

Aar*_*and 12

在你的而不是触发器中,你绝对可以获得插入的值......但直到你执行插入之后。

USE tempdb;
GO

CREATE TABLE dbo.SmellThis
(
  id INT IDENTITY(1,1),
  name VARCHAR(32)
);
GO

CREATE TRIGGER dbo.SmellThis_First
ON dbo.SmellThis
INSTEAD OF INSERT
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ids TABLE(id INT);

    IF NOT EXISTS 
    (
      SELECT 1 FROM sys.objects AS o
        INNER JOIN inserted AS i
        ON o.name = i.name
    )
    INSERT dbo.SmellThis(name)  
      OUTPUT inserted.id INTO @ids
      SELECT name 
      FROM inserted;

    SELECT id FROM @ids;
END
GO

INSERT dbo.SmellThis(name) SELECT 'Remus';
GO
Run Code Online (Sandbox Code Playgroud)

结果:

id
----
1
Run Code Online (Sandbox Code Playgroud)

现在清理:

DROP TABLE dbo.SmellThis;
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您永远不应该使用@@IDENTITYIDENT_CURRENT()无论如何。并且SCOPE_IDENTITY应该保留用于您知道只能插入一行的情况。触发器的一个常见误解是它们按行触发,就像在其他平台中一样,但在 SQL Server 中,它们按操作触发 - 所以使用VALUES(),(),()or的多行插入INSERT...SELECT-SCOPE_IDENTITY您会为变量设置哪个?


Rem*_*anu 8

使用 INSTEAD_OF 触发器意味着尚未发生插入。您无法知道身份,因为它尚未生成。从元数据中窃取值是可能的 ( DBCC CHECKIDENT) 但依赖它在并发下无法正常工作,此外还需要提升权限。

INSTEAD_OF 触发器极少需要,而且代码味道很重。你确定需要吗?您不能使用常规的 AFTER 触发器来完成这项工作吗?

  • 声明性的完整性总是比触发器更好。后触发器总是比代替触发器更好。触发器在很多情况下都具有“时髦”的行为,它们在 DML 中访问路径优化是不透明的,它们使隔离级别的行为不稳定。而不是触发器尖叫“我应该是一个访问存储过程而不是”。而且我根本不相信“做两次工作”的说法,优化*异常*路径不应该影响设计,特别是以减慢*频繁*路径为代价。 (4认同)
  • 您正在描述外键。插入子表应该是应用程序的责任,而不是触发器。从触发器执行它是糟糕的设计,无论如何它可以从正常的 AFTER 触发器完成。后触发器可能会引发错误并导致回滚,这是比替代触发器更好的选择。 (2认同)
  • 这是多么荒谬的想法——“不是触发器,而是严重的代码味道”?与后触发器相比,它们非常有用 - 如果您的业务规则被违反,您就完成了两次工作 - 您插入了行,然后将它们回滚。如果您的业务规则无法通过正常的 DRI 或其他约束来强制执行,则替代触发器可以阻止任何工作的发生。 (2认同)