Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / database / SQL-Server / SQL-Server-2012

Another Way to Get Identity from SQL Server

5.00/5 (1 vote)
26 Aug 2015CPOL2 min read 18.3K   19  
Different options to get Identity from SQL Server and how OUTPUT clause helped me to get the accurate Identity value compare with other options.

Introduction

Recently, I tried to get the newly inserted Identity value from SQL Server using @@IDENTITY, SCOPE_IDENTITY() and IDENT_CURRENT. By using these three options, I was not able to get the expected output. Here, I am trying to explain the different way we can get the identity value from SQL Server, and how it’s failed to produce my expected result. Finally, I am going to explain the best option to get the accurate identity value for current scenario.

Background

We have different options to get the Identity values but all options will not give the same/expected result. By knowing all the options, we can take the better decision.

@@IDENTITY

It returns the last identity value generated by any table any scope in connection. This value is either created by user or automatic trigger.

SQL
SELECT @@IDENTITY

IDENT_CURRENT(‘table name’)

It returns the last identity value generated by specific table. It does not depend on any scope or session.

SQL
SELECT IDENT_CURRENT('table name')

SCOPE_IDENTITY()

It returns the last identity value generated by any table within the current scope and current session. This will not take values from automatic trigger.

SQL
SELECT SCOPE_IDENTITY()

OUTPUT CLAUSE

Output clause returns the information from each row affected by Insert statement which includes Identity.

SQL
DECLARE @IdentityTableVariable TABLE (id INT)
INSERT INTO OutputTable OUTPUT inserted.id INTO @IdentityTableVariable
SELECT tbl1.ReferenceValue, tbl2.InputValue FROM ReferenceTable

How @@IDENTITY, SCOPE_IDENTITY(), IDENT_CURRENT() Failed

In the current scenario, I have a while loop which will get the value from Table1 and Table2 using INNER JOIN and insert a new record into Table3. Table3 also has an AFTER INSERT TRIGGER. Based on the Identity values, I will insert the records into Table4 and Table5 which is Foreign Key constraint with Table3. If I am not getting any Identity, then I should not try to insert any values into Table3 and Table4.

Sample

SQL
CREATE TABLE ReferenceTable (id INT NOT NULL PRIMARY KEY, ReferenceValue VARCHAR(50));
CREATE TABLE InputTable (id INT NOT NULL PRIMARY KEY, InputValue VARCHAR(50));
CREATE TABLE OutputTable (id INT NOT NULL identity, ReferenceValue VARCHAR(50), InputValue VARCHAR(50));

CREATE TRIGGER InsertRecordsToOutputTable ON OutputTable
AFTER INSERT
AS
BEGIN
INSERT INTO OutputTable VALUES ('InsertedByTrigger', 'InsertedByTrigger')
INSERT INTO OutputTable VALUES ('InsertedByTrigger1', 'InsertedByTrigger1')
END
SQL
INSERT INTO ReferenceTable VALUES (1, 'Record1');
INSERT INTO ReferenceTable VALUES (2, 'Record2');
INSERT INTO ReferenceTable VALUES (3, 'Record3');
INSERT INTO ReferenceTable VALUES (4, 'Record4');
INSERT INTO ReferenceTable VALUES (5, 'Record5');
INSERT INTO InputTable VALUES (1, 'Record1');
INSERT INTO InputTable VALUES (2, 'Record2');
INSERT INTO InputTable VALUES (3, 'Record3');
INSERT INTO InputTable VALUES (6, 'Record6');
INSERT INTO InputTable VALUES (5, 'Record5');
SQL
DECLARE @TotalRecord INT
DECLARE @CurrentRecord INT = 0;
SELECT @TotalRecord = count(1)FROM ReferenceTable

DECLARE @IdentityTableVariable TABLE (id INT)

WHILE (@CurrentRecord <= @TotalRecord)
BEGIN
      DELETE FROM @IdentityTableVariable
     SET @CurrentRecord = @CurrentRecord + 1;
         DECLARE @tempValue INT = NULL
         INSERT INTO OutputTable OUTPUT inserted.id
         INTO @IdentityTableVariable
         SELECT tbl1.ReferenceValue, tbl2.InputValue FROM ReferenceTable tbl1
         INNER JOIN InputTable tbl2 ON tbl1.id = tbl2.id WHERE tbl1.id = @CurrentRecord

SELECT tbl1.ReferenceValue, tbl2.InputValueFROM ReferenceTable tbl1
         INNER JOIN InputTable tbl2 ON tbl1.id = tbl2.id WHERE tbl1.id = @CurrentRecord
SELECT * FROM OutputTable WHERE id = @@IDENTITY
SELECT * FROM OutputTable WHERE id = SCOPE_IDENTITY()
SELECT ( SELECT id        FROM @IdentityTableVariable ) AS OutputClause, 
                 SCOPE_IDENTITY() AS ScopeIdentity, 
                 IDENT_CURRENT('OutputTable') AS IdentCurrent, 
                 @@IDENTITY AS AtIdentity
END

Output

Image 1

Option 1: @@IDENTITY

This failed because it includes identity value generated from Trigger.

Option 2: IDENT_CURRENT(‘table name’)

This one also failed for the same reason as above, it returns the Trigger generated value.

Option 3: SCOPE_IDENTITY()

Initially, this option seems to be working but when my INNER JOIN failed to produce a row, this option also failed. Because the expected result is NULL but SCOPE_IDENTITY() returns the previous loop identity value.

Option 4: OUTPUT CLAUSE

After doing some research, I found a new option which is OUTPUT CLAUSE.

This helps to get the expected result, i.e., when there is no insert, then return value is NULL.

Conclusion

As per Microsoft (https://support.microsoft.com/en-us/kb/2019779 ), @@IDENTITY and SCOPE_IDENTITY() may produce incorrect result in SQL Server 2005 and 2008, but this is not the reason for the current scenario.

By knowing different options to get the identity values from SQL Server, we can take better decision. In my case, the OUTPUT Clause is the most appropriate option.

History

  • 26th August, 2015: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)