如何使用代替触发器时插入最后一个身份行


9

当我使用而不是触发器插入表时@@IdentityIDENT_CURRENT('Table')SCOPE_IDENTITY()返回null。如何获得插入行的最后身份?


inserted,但没有行插入的时候INSTEAD OF触发触发器。
ypercubeᵀᴹ

检查此SO问题:可能有帮助。stackoverflow.com/q/908257/27535
gbn 2012年

您必须从“插入”中选择“选择ID”,.. Scope_Identity,@@ Identity将不起作用

Answers:


8

如果使用INSTEAD_OF触发器,则表示尚未发生插入。您无法识别该身份,因为尚未生成。可以从元数据中窃取值(DBCC CHECKIDENT),但是依赖它无法在并发下正常工作,此外它还需要提升的特权。

很少需要使用INSTEAD_OF触发器,并且不会产生严重的代码异味。确定要用吗?您不能使用常规的AFTER扳机进行工作吗?


我想控制插入行的数据完整性。在保存它们之前。如果数据不好,我提出错误比例信息。
mehdi lotfi 2012年

2
您正在描述外键。插入子表而不是触发器应由应用程序负责。从触发器执行此操作是错误的设计,无论如何,都可以从普通的AFTER触发器完成。After触发器可能会引发错误并导致回滚,这是比替代触发器更好的选择。
Remus Rusanu 2012年

1
什么荒谬的概念-“不是触发器而是严重的代码味道”?与after触发器相比,它们非常有用-在触发器中,如果违反了业务规则,则您需要完成两次工作-插入行,然后将它们回滚。如果您的业务规则不能通过正常的DRI或其他约束来强制执行,那么使用触发器代替触发器可以阻止任何工作发生。
亚伦·伯特兰

1
虽然“不是触发器而是严重的代码味道”,但这并不是严格的规定,它基于它可能在整个系统中引起的实际可能的问题。通常,业务规则破坏者不应在系统中发生,更不用说达到数据库级别了。在这种情况下,如果系统中存在漏洞,则触发器中的规则验证仅适合作为最后的防御机制。
Alireza 2012年

4
声明式完整性总是比触发器更好。后触发总是比后触发更好。相反,触发器在很多情况下都具有“笨拙”的行为,它们对DML中的访问路径优化不透明,它们使隔离级别的行为不稳定。触发触发器的尖叫声是“我应该是一个访问存储过程”。而且我根本不赞成“重复做两次”的论点,优化异常路径不会影响设计,特别是要减慢频繁路径的代价。
Remus Rusanu 2012年

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

结果:

id
----
1

现在清理:

DROP TABLE dbo.SmellThis;

顺便说一句,您永远都不要,永远不要使用@@IDENTITYIDENT_CURRENT()无论如何。并且SCOPE_IDENTITY应该保留给您只可以插入一行的情况。对触发器的一个普遍误解是,它们像在其他平台中一样按行触发,但在SQL Server中按操作触发-因此使用VALUES(),(),()或进行多行插入INSERT...SELECT- SCOPE_IDENTITY您将为变量设置哪个?


如何将插入记录的结果保存在变量表中以供以后使用。
mehdi lotfi 2012年

@mehdi您可以定义“以后”吗?您不能将列添加到上面已经声明的表变量中吗?
亚伦·伯特兰

3
我爱你的桌名。
Dan Esparza 2014年

-1

主要问题:触发器和实体框架都在不同的范围内工作。问题是,如果您在触发器中生成新的PK值,则它是不同的作用域。因此,此命令返回零行,并且EF将引发异常。

解决方案是在触发器的末尾添加以下SELECT语句:

SELECT * FROM deleted UNION ALL
SELECT * FROM inserted;

代替*您可以提及所有列名称,包括

SELECT IDENT_CURRENT(‘tablename’) AS <IdentityColumnname>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.