askso
askso

Reputation: 77

Inserted clause returns 0 when used with triggers

I'm trying to get the last inserted rows Id from an inserts statement on the following table using SQL server 2012

[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
)

I'm also using an audit triggers on that table that are as follows:

 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

I used different approaches, but nothing 'SAFE' seems to work.

Using scope identity returns null

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

Using OUTPUT INSERTED always returns 0 for the identity coloumns; although it returns the other inserted values:

 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

I know of another solution to get the inserted Id from the triggers itself, by executing a dynamic sql command as follows:

            declare @tmpTable table (id int)

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

            select id from @tmpTable

I couldn't figure out why in the first 2 cases it is not working; why the SCOPE_IDENTITY() does not work although the triggers execute in the same transaction? And also why the INSERTED clause returns 0 for the identity column.

Upvotes: 1

Views: 481

Answers (1)

Joel Allison
Joel Allison

Reputation: 2111

It appears that the following requirements apply to your audit column data:

  1. Use the insert value supplied for CreatedBy, or use SYSTEM_USER by default.
  2. Always use GETUTCDATE() for CreatedDate.

If the INSTEAD OF trigger (rather than an AFTER trigger) is not essential to your requirements, then you can use DEFAULT constraints on your audit columns and an AFTER INSERT trigger to enforce requirement #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

Then, both SCOPE_IDENTITY() and OUTPUT INSERTED techniques to get the new TableId value work as expected.

If the INSTEAD OF trigger is essential to your implementation, then SELECT @@IDENTITY is an alternative to SCOPE_IDENTITY.

Upvotes: 1

Related Questions