Which one is the best option to get the identity value I just generated via an insert? What is the impact of these statements in terms of performance?
- Aggregate function
TOP 1IdentityColumn FROM TableName
ORDER BY IdentityColumn DESC
SCOPE_IDENTITY() if you are inserting a single row and want to retrieve the ID that was generated.
CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1)); INSERT #a(x) VALUES('a'); SELECT SCOPE_IDENTITY();
OUTPUT clause if you are inserting multiple rows and need to retrieve the set of IDs that were generated.
INSERT #a(x) OUTPUT inserted.identity_column VALUES('b'),('c');
---- 2 3
and why this is best faster option?
Performance aside, these are the only ones that are guaranteed to be correct in the default isolation level and/or with multiple users. Even if you ignore the correctness aspect, SQL Server holds the inserted value in
SCOPE_IDENTITY() in memory, so naturally this will be faster than going and running your own isolated query against the table or against system tables.
Ignoring the correctness aspect is like telling the mailman he did a good job delivering today’s mail – he finished his route 10 minutes faster than his average time, the problem is, none of the mail was delivered to the right house.
Do not use any of the following:
@@IDENTITY– since this can’t be used in all scenarios, for example when a table with an identity column has a trigger that also inserts into another table with its own identity column – you will get the wrong value back.
IDENT_CURRENT()– I go into detail about this here, and the comments are useful reading as well, but essentially, under concurrency, you will often get the wrong answer.
TOP 1– you would have to protect the two statements with serializable isolation in order to ensure that the
MAX()you get isn’t someone else’s. This is much more expensive than just using
These functions also fail whenever you insert two or more rows, and need all the identity values generated – your only option there is the
Apart from performance, they all have rather different meanings.
SCOPE_IDENTITY() will give you the last identity value inserted into any table directly within the current scope (scope = batch, stored procedure, etc. but not within, say, a trigger that was fired by the current scope).
IDENT_CURRENT() will give you the last identity value inserted into a specific table from any scope, by any user.
@@IDENTITY gives you the last identity value generated by the most recent INSERT statement for the current connection, regardless of table or scope. (Side note: Access uses this function, and thus has some issues with triggers that insert values into tables with identity columns.)
TOP 1 can give you entirely wrong results if the table has a negative identity step, or has had rows inserted with
SET IDENTITY_INSERT in play. Here’s a script demonstrating all of these:
CREATE TABLE ReverseIdent ( id int IDENTITY(9000,-1) NOT NULL PRIMARY KEY CLUSTERED, data char(4) ) INSERT INTO ReverseIdent (data) VALUES ('a'), ('b'), ('c') SELECT * FROM ReverseIdent SELECT IDENT_CURRENT('ReverseIdent') --8998 SELECT MAX(id) FROM ReverseIdent --9000 SET IDENTITY_INSERT ReverseIdent ON INSERT INTO ReverseIdent (id, data) VALUES (9005, 'd') SET IDENTITY_INSERT ReverseIdent OFF SELECT IDENT_CURRENT('ReverseIdent') --8998 SELECT MAX(id) FROM ReverseIdent --9005
Summary: stick with
@@IDENTITY, and make sure you’re using the one that returns what you actually need.