They say you learn something new every day – well here’s what I found out today.
I have been guilty of using
select @@IDENTITY
in stored procedures (SQL Server) to return the ID of the record just inserted when the table uses an auto number field as the primary key.
@@IDENTITY returns the most recently created identity for your current connection, not necessarily the identity for the recently added row in a table. Always use SCOPE_IDENTITY() to return the identity of the recently added row.
This has issues when used in conjunction with triggers also. I have included an example below.
Triggers can be a useful mechanism for extending or integrating applications but the use of the above statement causes the original application problems in 2 ways:
- If the trigger inserts data into other tables the value is altered if the other table also uses auto numbering
- The original stored procedure starts to return dbNull rather than the ID
Sample Tables
CREATE TABLE dbo.Table1
(
id int NOT NULL IDENTITY (1, 1),
[desc] varchar(255) NULL,
what varchar(255) NULL
)
CREATE TABLE dbo.Table2
(
id int NOT NULL IDENTITY (1, 1),
[desc] varchar(255) NULL,
Extended varchar(255) NULL
)
Sample Stored Procedure
create procedure testing (@Desc varchar(255), @What varchar(255)) as
insert into table1 (desc, what) values (@Desc, @What)
select @@IDENTITY
return
Sample Trigger
CREATE TRIGGER Alert_Me ON [dbo].[tbl_sceneario]
FOR INSERT
AS
DECLARE @Desc varchar(255)
select @Desc = [Desc] from inserted
insert into table2 (desc, extended) values (@Desc, ‘Extended string’)
Problem 1.
@@Identity Changed
Because the trigger inserts into table2 which is using Auto numbered field it will set the @@Identity to its value.
Solution 1
To avoid this you should use SCOPE_IDENTITY()
This will return the last @@Identity generated by the stored procedure and not that from subsequent actions in the transaction
Problem 2.
Value returned is NULL
Due to the way SQL server operates, the trigger seems to affect @@IDENTITY and SCOPE_IDENTITY() and sets the value to NULL when returned as a rowset
Solution 2.
Add an output parameter to the procedure and set its value to SCOPE_IDENTITY
alter procedure testing (@Desc varchar(255), @What varchar(255), @ID int output) as
insert into table1 (desc, what) values (@Desc, @What)
set @ID = SCOPE_IDENTITY
return