如何使用INSTEAD OF触发器将新记录的标识插入到表中

Jum*_*zza 5 t-sql sql-server triggers scope-identity

我在表上使用INSTEAD OF插入触发器来设置行上的递增版本号,并将行复制到第二个历史/审计表.
行没有问题地插入到两个表中.
但是,我无法将第一个表中的新标识返回给用户.

架构

CREATE TABLE Table1
(
   id INT IDENTITY(1,1) PRIMARY KEY,
   name VARCHAR(250) NOT NULL UNIQUE,
   rowVersion INT NOT NULL
)

CREATE TABLE Table1History
(
   id INT NOT NULL,
   name VARCHAR(250) NOT NULL,
   rowVersion INT NOT NULL
)   

CREATE TRIGGER TRG_INS_Table1
ON Table1
INSTEAD OF INSERT
AS
    DECLARE @OutputTbl TABLE (id INT, name VARCHAR(250))
BEGIN
--make the insert
INSERT INTO Table1 (name, rowVersion)
    OUTPUT INSERTED.id, INSERTED.name INTO @OutputTbl(id, name)
    SELECT i.name, 1
    FROM INSERTED i
--copy into history table
INSERT INTO Table1History (id, name, rowVersion)
    SELECT t.ID, i.name, 1
    FROM INSERTED i
    JOIN @OutputTbl t on i.name = t.name
END

CREATE TRIGGER TRG_UPD_Table1
ON Table1
INSTEAD OF UPDATE
AS
BEGIN
--make the update
UPDATE Table1
    SET name = i.name,
        rowVersion = (SELECT d.rowVersion + 1 FROM DELETED d WHERE d.id = i.id)
    FROM INSERTED i
    WHERE Table1.id = i.id
--copy into history table
INSERT INTO Table1History (id, name, rowVersion)
    SELECT i.id ,i.name, (SELECT d.rowVersion + 1 FROM DELETED d WHERE d.id = i.id)
    FROM INSERTED i
END
Run Code Online (Sandbox Code Playgroud)

name在插入触发器中连接列并不理想,但它需要同时处理多个插入.
例如INSERT INTO Table1 (name) VALUES('xxx'),('yyy')

试图解决方案

做插入时,SCOPE_IDENTITYNULL.

INSERT INTO Table1(name)
VALUES('xxx')
SELECT SCOPE_IDENTITY() 

or 

INSERT INTO Table1(name)
VALUES('xxx')
RETURN SCOPE_IDENTITY()
Run Code Online (Sandbox Code Playgroud)

我也尝试过使用OUTPUT - 它返回0:

DECLARE @IdentityOutput TABLE (id INT)
INSERT INTO Table1(name)
OUTPUT INSERTED.id INTO @IdentityOutput
VALUES('xxx')
SELECT id FROM @IdentityOutput
Run Code Online (Sandbox Code Playgroud)

行插入正常并有ID,但我不能访问它们,除非我使用下面 - 这似乎hacky:

INSERT INTO Table1(name)
VALUES('xxx')
SELECT id from Table1 WHERE name = 'xxx' 
Run Code Online (Sandbox Code Playgroud)

获取新ID的正确方法是什么?


不可能!在具有INSTEAD OF触发器的表上执行INSERT时,无法可靠地返回标识.下面的Sidux答案是我的情况的一个很好的解决方法(用AFTER触发器替换INSTEAD OF触发器并添加DEFAULT列).

Dmi*_*sev 2

CREATE TABLE Table1
(
   id INT IDENTITY(1,1) PRIMARY KEY,
   name VARCHAR(250) NOT NULL UNIQUE,
   rowVersion INT NOT NULL
)
GO
CREATE TABLE Table1History
(
   id INT NOT NULL,
   name VARCHAR(250) NOT NULL,
   rowVersion INT NOT NULL
)   
GO
CREATE TRIGGER TRG_INS_Table1
ON Table1
INSTEAD OF INSERT
AS
    DECLARE @OutputTbl TABLE (id INT, name VARCHAR(250))
BEGIN
--make the insert
INSERT INTO Table1 (name, rowVersion)
    SELECT i.name, 1
    FROM INSERTED i
END
GO
CREATE TRIGGER TRG_UPD_Table1
ON Table1
INSTEAD OF UPDATE
AS
BEGIN
--make the update
UPDATE Table1
    SET name = i.name,
        rowVersion = (SELECT d.rowVersion + 1 FROM DELETED d WHERE d.id = i.id)
    FROM INSERTED i
    WHERE Table1.id = i.id
END
GO
CREATE TRIGGER TRG_AFT_INS_Table1
ON Table1
AFTER INSERT, UPDATE
AS

BEGIN

INSERT INTO Table1History (id, name, rowVersion)

    SELECT i.ID, i.name, i.rowversion
    FROM INSERTED i

END
GO

INSERT INTO Table1 (name) VALUES('xxx'),('yyy')

SELECT * FROM Table1History
-----------------------------------------------
id  name    rowVersion
2   yyy 1
1   xxx 1
-----------------------------------------------

UPDATE Table1 SET name = 'xxx1' WHERE id  = 1;

SELECT * FROM Table1History
-----------------------------------------------
id  name    rowVersion
2   yyy 1
1   xxx 1
1   xxx1    2
-----------------------------------------------
Run Code Online (Sandbox Code Playgroud)

基本上您不需要 TRG_INS_Table1 触发器,您只需对列使用 DEFAULT value = 1 即可。此外,如果您使用 DATETIME 列而不是 rowversion,则可以使用 GETDATE() 值将 INSERTED 表的状态插入到历史记录中。在这种情况下,您可以按 Dtime 列 DESC 进行排序,并且您有历史记录。