与触发器一起使用时,Inserted子句返回0

时间:2014-04-30 21:23:59

标签: sql sql-server

我尝试使用SQL Server 2012从下表中的insert语句中获取最后插入的行ID

[dbo].[Table](
[TableId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NULL,
[CreatedBy] [nvarchar](50) NULL,
[CreatedDate] [datetime2](7) NOT NULL,
[ModifiedBy] [nvarchar](50) NULL,
[ModifiedDate] [datetime2](7) NULL,
CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED 
(
[TableId] ASC
)

我还在该表上使用了如下审计触发器:

 trigger [dbo].[trigger_Table_auditColumnAutoInsert]
on [dbo].[Table]
instead of insert
/************************************************************** 
* INSTEAD OF trigger on table [dbo].[Table] responsible
    for automatically inserting audit column data
**************************************************************/ 
as
begin
    set nocount on
    declare @currentTime datetime2
    set @currentTime = GETUTCDATE()

    insert into [dbo].[Table]
    (
        Name,
        CreatedBy,
        CreatedDate,
        ModifiedBy,
        ModifiedDate
    )
    select
        Name,
        ISNULL(CreatedBy, system_user),
        @currentTime,
        NULL,
        NULL
    from inserted
    select SCOPE_IDENTITY() as [TableId]
    goto EOP -- end of procedure

ErrorHandler:
    if (@@trancount <> 0) rollback tran
EOP:
end

我使用了不同的方法,但没有什么&#39;安全&#39;似乎工作。

使用范围标识返回null

insert into dbo.[Table](Name) Values('foo')
select SCOPE_IDENTITY()

使用OUTPUT INSERTED始终为身份颜色返回0;虽然它返回其他插入的值:

 declare @tmpTable table
 (
 TableId int,
 Name nvarchar (50)
 )

INSERT INTO [dbo].[Table]([Name])
output inserted.TableId, inserted.Name into @tmpTable
VALUES('foo')


select * from @tmpTable

TableId Name
0       foo

我知道通过执行动态sql命令从触发器本身获取插入的Id的另一个解决方案,如下所示:

            declare @tmpTable table (id int)

            insert @tmpTable (id )
            exec sp_executesql N'insert into dbo.[Table](Name) Values(''foo'')'

            select id from @tmpTable

我无法弄清楚为什么在前两种情况下它不起作用;虽然触发器在同一个事务中执行,但为什么SCOPE_IDENTITY()不起作用?以及为什么INSERTED子句为标识列返回0。

1 个答案:

答案 0 :(得分:1)

以下要求似乎适用于您的审核列数据:

  1. 使用为CreatedBy提供的插入值,或默认情况下使用SYSTEM_USER
  2. 始终将GETUTCDATE()用于CreatedDate
  3. 如果INSTEAD OF触发器(而不是AFTER触发器)对您的要求不是必不可少的,那么您可以在审计列上使用DEFAULT约束,并使用AFTER INSERT触发器来强制执行要求#2。

    CREATE TABLE [dbo].[Table]
    (
        [TableId] [int] IDENTITY(1,1) NOT NULL,
        [Name] [nvarchar](50) NULL,
        [CreatedBy] [nvarchar](50) NOT NULL CONSTRAINT [DF_Table_CreatedBy] DEFAULT SYSTEM_USER,
        [CreatedDate] [datetime2](7) NOT NULL CONSTRAINT [DF_Table_CreatedDate] DEFAULT GETUTCDATE(),
        [ModifiedBy] [nvarchar](50) NULL,
        [ModifiedDate] [datetime2](7) NULL,
        CONSTRAINT [pk_Table] PRIMARY KEY CLUSTERED ([TableId] ASC)
    )
    GO
    
    CREATE TRIGGER Trigger_Table_AfterInsert ON [dbo].[Table]
    AFTER INSERT
    AS
    BEGIN
        SET NOCOUNT ON
        UPDATE [dbo].[Table] SET [CreatedDate]=GETUTCDATE()
        FROM [dbo].[Table] AS T
        INNER JOIN INSERTED AS I ON I.[TableId]=T.[TableId]
    END
    GO
    

    然后,SCOPE_IDENTITY()和OUTPUT INSERTED技术都可以使新的TableId值按预期工作。

    如果INSTEAD OF触发器 对您的实施至关重要,那么SELECT @@IDENTITY可替代SCOPE_IDENTITY