How am I supposed to get the IDENTITY of an inserted row?
I know about ##IDENTITY and IDENT_CURRENT and SCOPE_IDENTITY, but don't understand the implications or impacts attached to each.
Can someone please explain the differences and when I would be using each?
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes. You need to be careful here, since it's across scopes. You could get a value from a trigger, instead of your current statement.
SCOPE_IDENTITY() returns the last identity value generated for any table in the current session and the current scope. Generally what you want to use.
IDENT_CURRENT('tableName') returns the last identity value generated for a specific table in any session and any scope. This lets you specify which table you want the value from, in case the two above aren't quite what you need (very rare). Also, as #Guy Starbuck mentioned, "You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into."
The OUTPUT clause of the INSERT statement will let you access every row that was inserted via that statement. Since it's scoped to the specific statement, it's more straightforward than the other functions above. However, it's a little more verbose (you'll need to insert into a table variable/temp table and then query that) and it gives results even in an error scenario where the statement is rolled back. That said, if your query uses a parallel execution plan, this is the only guaranteed method for getting the identity (short of turning off parallelism). However, it is executed before triggers and cannot be used to return trigger-generated values.
I believe the safest and most accurate method of retrieving the inserted id would be using the output clause.
for example (taken from the following MSDN article)
USE AdventureWorks2008R2;
GO
DECLARE #MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO #MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM #MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
I'm saying the same thing as the other guys, so everyone's correct, I'm just trying to make it more clear.
##IDENTITY returns the id of the last thing that was inserted by your client's connection to the database.
Most of the time this works fine, but sometimes a trigger will go and insert a new row that you don't know about, and you'll get the ID from this new row, instead of the one you want
SCOPE_IDENTITY() solves this problem. It returns the id of the last thing that you inserted in the SQL code you sent to the database. If triggers go and create extra rows, they won't cause the wrong value to get returned. Hooray
IDENT_CURRENT returns the last ID that was inserted by anyone. If some other app happens to insert another row at an unforunate time, you'll get the ID of that row instead of your one.
If you want to play it safe, always use SCOPE_IDENTITY(). If you stick with ##IDENTITY and someone decides to add a trigger later on, all your code will break.
The best (read: safest) way to get the identity of a newly-inserted row is by using the output clause:
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare #IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into #IdentityOutput
values
( ... )
select #IdentityValue = (select ID from #IdentityOutput)
Add
SELECT CAST(scope_identity() AS int);
to the end of your insert sql statement, then
NewId = command.ExecuteScalar()
will retrieve it.
From MSDN
##IDENTITY, SCOPE_IDENTITY, and IDENT_CURRENT are similar functions in that they return the last value inserted into the IDENTITY column of a table.
##IDENTITY and SCOPE_IDENTITY will return the last identity value generated in any table in the current session. However, SCOPE_IDENTITY returns the value only within the current scope; ##IDENTITY is not limited to a specific scope.
IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope. For more information, see IDENT_CURRENT.
IDENT_CURRENT is a function which takes a table as a argument.
##IDENTITY may return confusing result when you have an trigger on the table
SCOPE_IDENTITY is your hero most of the time.
When you use Entity Framework, it internally uses the OUTPUT technique to return the newly inserted ID value
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, joined back to the table, and return the row value out of the table.
Note: I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
This technique (OUTPUT) is only available on SQL Server 2008 or newer.
Edit - The reason for the join
The reason that Entity Framework joins back to the original table, rather than simply use the OUTPUT values is because EF also uses this technique to get the rowversion of a newly inserted row.
You can use optimistic concurrency in your entity framework models by using the Timestamp attribute: 🕗
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
When you do this, Entity Framework will need the rowversion of the newly inserted row:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO #generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM #generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE ##ROWCOUNT > 0
And in order to retrieve this Timetsamp you cannot use an OUTPUT clause.
That's because if there's a trigger on the table, any Timestamp you OUTPUT will be wrong:
Initial insert. Timestamp: 1
OUTPUT clause outputs timestamp: 1
trigger modifies row. Timestamp: 2
The returned timestamp will never be correct if you have a trigger on the table. So you must use a separate SELECT.
And even if you were willing to suffer the incorrect rowversion, the other reason to perform a separate SELECT is that you cannot OUTPUT a rowversion into a table variable:
DECLARE #generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO #generated_keys
VALUES('Malleable logarithmic casing');
The third reason to do it is for symmetry. When performing an UPDATE on a table with a trigger, you cannot use an OUTPUT clause. Trying do UPDATE with an OUTPUT is not supported, and will give an error:
Cannot use UPDATE with OUTPUT clause when a trigger is on the table
The only way to do it is with a follow-up SELECT statement:
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE ##ROWCOUNT > 0 AND TurboEncabulatorID = 1
I can't speak to other versions of SQL Server, but in 2012, outputting directly works just fine. You don't need to bother with a temporary table.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
By the way, this technique also works when inserting multiple rows.
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
Output
ID
2
3
4
##IDENTITY is the last identity inserted using the current SQL Connection. This is a good value to return from an insert stored procedure, where you just need the identity inserted for your new record, and don't care if more rows were added afterward.
SCOPE_IDENTITY is the last identity inserted using the current SQL Connection, and in the current scope -- that is, if there was a second IDENTITY inserted based on a trigger after your insert, it would not be reflected in SCOPE_IDENTITY, only the insert you performed. Frankly, I have never had a reason to use this.
IDENT_CURRENT(tablename) is the last identity inserted regardless of connection or scope. You could use this if you want to get the current IDENTITY value for a table that you have not inserted a record into.
ALWAYS use scope_identity(), there's NEVER a need for anything else.
One other way to guarantee the identity of the rows you insert is to specify the identity values and use the SET IDENTITY_INSERT ON and then OFF. This guarantees you know exactly what the identity values are! As long as the values are not in use then you can insert these values into the identity column.
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT ##Identity AS [##Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
This can be a very useful technique if you are loading data from another source or merging data from two databases etc.
Create a uuid and also insert it to a column. Then you can easily identify your row with the uuid. Thats the only 100% working solution you can implement. All the other solutions are too complicated or are not working in same edge cases.
E.g.:
1) Create row
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2) Get created row
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
Even though this is an older thread, there is a newer way to do this which avoids some of the pitfalls of the IDENTITY column in older versions of SQL Server, like gaps in the identity values after server reboots. Sequences are available in SQL Server 2016 and forward which is the newer way is to create a SEQUENCE object using TSQL. This allows you create your own numeric sequence object in SQL Server and control how it increments.
Here is an example:
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
Then in TSQL you would do the following to get the next sequence ID:
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
Here are the links to CREATE SEQUENCE and NEXT VALUE FOR
Complete solution in SQL and ADO.NET
const string sql = "INSERT INTO [Table1] (...) OUTPUT INSERTED.Id VALUES (...)";
using var command = connection.CreateCommand();
command.CommandText = sql;
var outputIdParameter = new SqlParameter("#Id", SqlDbType.Int) { Direction = ParameterDirection.Output };
command.Parameters.Add(outputIdParameter);
await connection.OpenAsync();
var outputId= await command.ExecuteScalarAsync();
await connection.CloseAsync();
int id = Convert.ToInt32(outputId);
After Your Insert Statement you need to add this. And Make sure about the table name where data is inserting.You will get current row no where row affected just now by your insert statement.
IDENT_CURRENT('tableName')
Related
I have two tables that are identical except one has an identity column and the other doesn't. Instead the second table uses the value of the identity column from the first table. I thought I would insert into the second table as a trigger when a record is inserted into the first table. I cannot seem to get syntax right.
Null is being returned from the identity column #EDVisitId.
ALTER TRIGGER [dbo].[trgInserterrEDVisitOriginal] ON [dbo].[errEDVisit]
AFTER INSERT
AS
--Name: Bob Avallone
--Date: 6-15-2017
--
-- The purpose of this trigger is to insert a record into errEDVisitOriginal
-- whenever a new errEDVisit is inserted.
--XXXXXXXXXX
declare #EDVisitId int
declare #SubmissionControlID INT
Select #EDVisitId = EDVisitID from inserted
SELECT #SubmissionControlID = SubmissionControlID from Inserted
Begin
Insert Into errEDVisitOriginal (EDVisitId, SubmissionControlID)
VALUES (#EDVisitId, #SubmissionControlID )
End
Thanks for all the suggestions. I abandoned the idea of a trigger. Instead I simply insert the new records from the first table into the second one. See below.
Insert errEDVisitOriginal(EdVisitId, SubmissionControlID)
Select EdVisitId, SubmissionControlID
from errEDVisit where SubmissionControlID = #SubmissionControlID
you just need scope_identity()
Select #EDVisitId = scope_identity()
You can use SCOPE_IDENTITY to get the last inserted id from the first table put it into a variable then insert into the second table.
https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql
Though the decision to duplicate information intentionally is dubious, you are vastly over-thinking the code. Just:
if exists (select * from inserted)
insert dbo.errEDVisitOriginal (EDVisitId, SubmissionControlID)
select EDVisitId, SubmissionControlID from inserted;
I have seen various methods used when retrieving the value of a primary key identity field after insert.
declare #t table (
id int identity primary key,
somecol datetime default getdate()
)
insert into #t
default values
select SCOPE_IDENTITY() --returns 1
select ##IDENTITY --returns 1
Returning a table of identities following insert:
Create Table #Testing (
id int identity,
somedate datetime default getdate()
)
insert into #Testing
output inserted.*
default values
What method is proper or better? Is the OUTPUT method scope-safe?
The second code snippet was borrowed from SQL in the Wild
It depends on what you are trying to do...
##IDENTITY
Returns the last IDENTITY value produced on a connection, regardless of the table that produced the value, and regardless of the scope of the statement that produced the value.
##IDENTITY will return the last identity value entered into a table in your current session. ##IDENTITY is limited to the current session and is not limited to the current scope. For example, if you have a trigger on a table that causes an identity to be created in another table, you will get the identity that was created last, even if it was the trigger that created it.
SCOPE_IDENTITY()
Returns the last IDENTITY value produced on a connection and by a statement in the same scope, regardless of the table that produced the value.
SCOPE_IDENTITY() is similar to ##IDENTITY, but it will also limit the value to your current scope. In other words, it will return the last identity value that you explicitly created, rather than any identity that was created by a trigger or a user defined function.
IDENT_CURRENT()
Returns the last IDENTITY value produced in a table, regardless of the connection and scope of the statement that produced the value. IDENT_CURRENT is limited to a specified table, but not by connection or scope.
Note that there is a bug in scope_identity() and ##identity - see MS Connect: https://web.archive.org/web/20130412223343/https://connect.microsoft.com/SQLServer/feedback/details/328811/scope-identity-sometimes-returns-incorrect-value
A quote (from Microsoft):
I highly recommend using OUTPUT instead of ##IDENTITY in all cases.
It's just the best way there is to read identity and timestamp.
Edited to add: this may be fixed now. Connect is giving me an error, but see:
Scope_Identity() returning incorrect value fixed?
There is almost no reason to use anything besides an OUTPUT clause when trying to get the identity of the row(s) just inserted. The OUTPUT clause is scope and table safe.
Here's a simple example of getting the id after inserting a single row...
DECLARE #Inserted AS TABLE (MyTableId INT);
INSERT [MyTable] (MyTableColOne, MyTableColTwo)
OUTPUT Inserted.MyTableId INTO #Inserted
VALUES ('Val1','Val2')
SELECT MyTableId FROM #Inserted
Detailed docs for OUTPUT clause: http://technet.microsoft.com/en-us/library/ms177564.aspx
-- table structure for example:
CREATE TABLE MyTable (
MyTableId int NOT NULL IDENTITY (1, 1),
MyTableColOne varchar(50) NOT NULL,
MyTableColTwo varchar(50) NOT NULL
)
##Identity is the old school way. Use SCOPE_IDENTITY() in all instances going forward. See MSDN for the repercussions of using ##IDENTITY (they're bad!).
SCOPE_IDENTITY is sufficient for single rows and is recommended except in cases where you need to see the result of an intermediate TRIGGER for some reason (why?).
For multiple rows, OUTPUT/OUTPUT INTO is your new best friend and alternative to re-finding the rows and inserting into another table.
There is another method available in SQL Server 2005 that is outlined in SQL in the Wild.
This will allow you to retrieve multiple identities after insert. Here's the code from the blog post:
Create Table #Testing (
id int identity,
somedate datetime default getdate()
)
insert into #Testing
output inserted.*
default values
A small correction to Godeke's answer:
It's not just triggers you need to worry about. Any kind of nested operation, such as stored procs, that causes identifiers to be created could change the value of ##IDENTITY.
Another vote for scope_identity...
Be carreful while using ##IDENTITY ...
http://dotnetgalactics.wordpress.com/2009/10/28/scope-identity-vs-identity/
I have a query where a record gets added to the table channels. Each record there is then assigned a channelID which is the PK for the table, and auto increments. After a record is added to the channel table I want to insert strings associate with that record into another table called channel keywords. My problem is that I also need to pass in the channelID that created in the previous query as a parameter for the insert statement for the channelskeywords table, but I am not quite sure how to do this.
IF #matchBy ='title'
BEGIN
insert into channels
(userID,matchTitle,matchTitleAbstract, fromMyPage)
values
(#userID,1,0,#fromMyPage)
END
IF #matchBy ='TitleAbstract'
insert into channels
(userID,matchTitle,matchTitleAbstract, fromMyPage)
values
(#userID,0,1,#fromMyPage)
BEGIN
IF (NULLIF(#keyword1, '')) IS NOT NULL
insert into channelsKeywords
Try something like this
BEGIN
-- be sure to declare this same scope as the one whol will be using the value
DECLARE #CNT INT
insert into channels (userID,matchTitle,matchTitleAbstract, fromMyPage)
values (#userID,1,0,#fromMyPage)
SET #CNT = Scope_Identity() -- holds the value of the inserted ID
END
Additional Information:
##IDENTITY returns the last identity value generated for any table in the current session, across all scopes. You need to be careful here, since it's across scopes. You could get a value from a trigger, instead of your current statement.
SCOPE_IDENTITY returns the last identity value generated for any table in the current session and the current scope. Generally what you want to use.
IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope. This lets you specify which table you want the value from, in case the two above aren't quite what you need (very rare).
See also here
You can get the id by
Select Scope_Identity()
ie:
insert into channels
(userID,matchTitle,matchTitleAbstract, fromMyPage)
values
(#userID,1,0,#fromMyPage)
select #ChannelID = Scope_Identity()
How can I get back the autogenerated ID for a new record I just inserted?
(Using ASP classic and MSSQL 2005)
SELECT SCOPE_IDENTITY()
using ##IDENTITY can have unexpected results, so be careful how you use that one. Triggers that insert records to other tables will cause the ##IDENTITY value to change - where SCOPE_IDENTITY() will give you the last identity from only your current scope.
Here's a sample that'll show the difference between ##IDENTITY and SCOPE_INSERT() and how they can return different values..
use tempdb
go
create table table1
(ID int identity)
go
create table table2
(ID int identity(100, 1))
go
create trigger temptrig
on table1
for insert
as
begin
insert table2
default values;
end
go
insert table1
default values;
select SCOPE_IDENTITY(),
##IDENTITY
Another option that nobody has discussed here is to use the OUTPUT clause that is in SQL 2005. In this case, you'd just have to add the output clause to your insert, and then catch that recordset from your code. This works well when inserting multiple records instead of just 1...
use tempdb
go
create table table1
(ID int identity)
go
insert table1
output inserted.ID
default values;
--OR...
insert table1
output inserted.$identity
default values;
SELECT ##IDENTITY usually works, but could return the identity of a record inserted because of a trigger or something, and not the original.
SELECT SCOPE_IDENTITY is what I'd recommend. It returns values inserted only within the current scope.
There is also a "IDENT_CURRENT(tablename)" that returns the last identity inserted for a specific table.
There are three ways to get the the last identity in sql.
They were already mentioned by others, but for completeness:
##IDENTITY - can also return ids created in other objects in the same scope (think triggers)
IDENT_CURRENT - limited to a table, but not to your scope, so it can give bad results for busy tables
Scope_Idenity() - Limited to the scope of the request. Use this 99% of the time
Additionally, there are three ways to take that ID and return it to your client code:
Use an output parameter in a stored procedure
INSERT INTO [MyTable] ([col1],[col2],[col3]) VALUES (1,2,3);
SELECT #OutputParameterName = Scope_Identity();
Use a return value.
INSERT INTO [MyTable] ([col1],[col2],[col3]) VALUES (1,2,3);
Return Scope_Identity();
Select the id into a result set. For example, your sql statement would look something like this:
Dim ResultID As Integer
Dim strSQL As String
strSQL = "INSERT INTO [MyTable] ([col1],[col2],[col3]) VALUES (1,2,3); SELECT Scope_Identity();"
rsResults.Open strSQL, oConn
ResultID = rsResults("ID")
Unfortunately (or fortunately, from my point of view) my Classic ASP it too far gone to show examples of the first two from client code.
SELECT ##Identity or SELECT SCOPE_IDENTITY() both work however Selecting SCOPE_Identity() is safer because it returns the last auto generated ID within your current scope. So for example assume we have a table called ScopeIDTable and on this table we have a trigger. This trigger will insert into a record into TriggerIdTable both tables have an auto increment column.
If you use SELECT ##Identity you will get the last auto increment in that session which would be the Id generated from within the trigger (TriggerIdTable).
If you use SELECT SCOPE_IDENTITY() you will get the id from your ScopeIdTable.
You run the query
select scope_identity()
using the same database connection, before doing anything else with it. The result is, as you probably expect, a record set containing a single row that has a single field. You can access the field using index 0, or you can give it a name if you prefer that:
select scope_identity() as lastId
I always wondered why one would ever want to use
##identity
since
select scope_identity()
obviously is the most save way to accomplish what Scot is asking for.
Where multiple records need to inserted at once in a set-based fashion, it can get more interesting.
I have sometimes used GUIDs generated clientside (but for classic ASP you'd probably need to use a utility to generate the values) or, more often, a NEWSQUENTIALID() constraint on the GUID key column at the server end.
I'm aware not everyone like GIUDS though, for some quite valid reasons (their size and how it affects indexing/paging for one).
http://www.sqlmag.com/Articles/Index.cfm?ArticleID=50164&pg=2
Thanks all who suggested SELECT SCOPE_IDENTITY(). I was able to create a stored procedure:
USE [dbname]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[spInsert]
(
#Nn varchar(30)
)
AS
BEGIN TRANSACTION InsertRecord
INSERT INTO A (Nn)
VALUES (#Nn)
SELECT NewID = SCOPE_IDENTITY() -- returns the new record ID of this transaction
COMMIT TRANSACTION InsertRecord
and call the sproc using VB:
Dim strNn '<- var to be passed'
Set cn = Server.CreateObject("ADODB.Connection")
connectString = "DSN"
cn.Open connectString, "user", "PW0rd"
Set rs = Server.CreateObject("ADODB.Recordset")
set rs = cn.Execute("EXEC [dbname].[dbo].[A] #Nn=" & strNn)
'return the value'
resultID = rs(0)
I can now use resultID anytime I refer to the newly created ID.
I have seen various methods used when retrieving the value of a primary key identity field after insert.
declare #t table (
id int identity primary key,
somecol datetime default getdate()
)
insert into #t
default values
select SCOPE_IDENTITY() --returns 1
select ##IDENTITY --returns 1
Returning a table of identities following insert:
Create Table #Testing (
id int identity,
somedate datetime default getdate()
)
insert into #Testing
output inserted.*
default values
What method is proper or better? Is the OUTPUT method scope-safe?
The second code snippet was borrowed from SQL in the Wild
It depends on what you are trying to do...
##IDENTITY
Returns the last IDENTITY value produced on a connection, regardless of the table that produced the value, and regardless of the scope of the statement that produced the value.
##IDENTITY will return the last identity value entered into a table in your current session. ##IDENTITY is limited to the current session and is not limited to the current scope. For example, if you have a trigger on a table that causes an identity to be created in another table, you will get the identity that was created last, even if it was the trigger that created it.
SCOPE_IDENTITY()
Returns the last IDENTITY value produced on a connection and by a statement in the same scope, regardless of the table that produced the value.
SCOPE_IDENTITY() is similar to ##IDENTITY, but it will also limit the value to your current scope. In other words, it will return the last identity value that you explicitly created, rather than any identity that was created by a trigger or a user defined function.
IDENT_CURRENT()
Returns the last IDENTITY value produced in a table, regardless of the connection and scope of the statement that produced the value. IDENT_CURRENT is limited to a specified table, but not by connection or scope.
Note that there is a bug in scope_identity() and ##identity - see MS Connect: https://web.archive.org/web/20130412223343/https://connect.microsoft.com/SQLServer/feedback/details/328811/scope-identity-sometimes-returns-incorrect-value
A quote (from Microsoft):
I highly recommend using OUTPUT instead of ##IDENTITY in all cases.
It's just the best way there is to read identity and timestamp.
Edited to add: this may be fixed now. Connect is giving me an error, but see:
Scope_Identity() returning incorrect value fixed?
There is almost no reason to use anything besides an OUTPUT clause when trying to get the identity of the row(s) just inserted. The OUTPUT clause is scope and table safe.
Here's a simple example of getting the id after inserting a single row...
DECLARE #Inserted AS TABLE (MyTableId INT);
INSERT [MyTable] (MyTableColOne, MyTableColTwo)
OUTPUT Inserted.MyTableId INTO #Inserted
VALUES ('Val1','Val2')
SELECT MyTableId FROM #Inserted
Detailed docs for OUTPUT clause: http://technet.microsoft.com/en-us/library/ms177564.aspx
-- table structure for example:
CREATE TABLE MyTable (
MyTableId int NOT NULL IDENTITY (1, 1),
MyTableColOne varchar(50) NOT NULL,
MyTableColTwo varchar(50) NOT NULL
)
##Identity is the old school way. Use SCOPE_IDENTITY() in all instances going forward. See MSDN for the repercussions of using ##IDENTITY (they're bad!).
SCOPE_IDENTITY is sufficient for single rows and is recommended except in cases where you need to see the result of an intermediate TRIGGER for some reason (why?).
For multiple rows, OUTPUT/OUTPUT INTO is your new best friend and alternative to re-finding the rows and inserting into another table.
There is another method available in SQL Server 2005 that is outlined in SQL in the Wild.
This will allow you to retrieve multiple identities after insert. Here's the code from the blog post:
Create Table #Testing (
id int identity,
somedate datetime default getdate()
)
insert into #Testing
output inserted.*
default values
A small correction to Godeke's answer:
It's not just triggers you need to worry about. Any kind of nested operation, such as stored procs, that causes identifiers to be created could change the value of ##IDENTITY.
Another vote for scope_identity...
Be carreful while using ##IDENTITY ...
http://dotnetgalactics.wordpress.com/2009/10/28/scope-identity-vs-identity/