437

I'm trying to get a the key-value back after an INSERT-statement. Example: I've got a table with the attributes name and id. id is a generated value.

    INSERT INTO table (name) VALUES('bob');

Now I want to get the id back in the same step. How is this done?

We're using Microsoft SQL Server 2008.

2

15 Answers 15

647

No need for a separate SELECT...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

This works for non-IDENTITY columns (such as GUIDs) too

Sign up to request clarification or add additional context in comments.

8 Comments

could you elaborate a bit? Where does the Output go in this example? The documentation only shows examples for tables (using output... into). Ideally I'd like to just be able to pass it into a variable
@JonnyLeeds: you can't do it to a variable (unless a table variable). The OUTPUT goes to the client or a table
Unfortunately, you can't rely on this since adding a trigger to the table will break your statements! re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
@hajikelist: this is quite an edge case, SET NCOOUNT ON in the trigger usually helps. See stackoverflow.com/questions/1483732/set-nocount-on-usage
Never use @@IDENTITY. SCOPE_IDENTITY, yes, but never @@IDENTITY. It is unreliable
|
263

Use SCOPE_IDENTITY() to get the new ID value

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx

4 Comments

@liho1eye - The OP referred to the identity column name as id, so yes.
On larger system, what if many sql's run at same time? Will it return the last inserted id to every request?
@Shiv "SCOPE_IDENTITY returns values inserted only within the current scope"
@dwp4ge "A scope is a module: a stored procedure, trigger, function, or batch" according to MS' doc. Does it include a transaction?
59
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

Is the safest bet since there is a known issue with OUTPUT Clause conflict on tables with triggers. Makes this quite unreliable as even if your table doesn't currently have any triggers - someone adding one down the line will break your application. Time Bomb sort of behaviour.

See msdn article for deeper explanation:

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx

6 Comments

Only if you don't add SET NOCOUNT ON in triggers. Also see learn.microsoft.com/en-us/sql/database-engine/configure-windows/…
this is not an option for our legacy environments @gbn
@hajikelist We all have legacy, but the risk of triggers messing OUTPUT up is low, all it takes is set nocount on. If someone is adding a trigger then they should know how to code it (implies you have control mainly), or you need to train your developers.. At some point, you'll be forced to migrate when that version of SQL is no longer supported etc so triggers won't cause a resultset. Whatever, it is not the best answer because if you have INSTEAD OF triggers the SCOPE_IDENTITY might not work (stackoverflow.com/questions/908257/…)
@gbn - I just like to avoid silly things like this. I'm not going to tell all my developers, "Don't forget to add the 'don't break my app statement' in every trigger." - you can keep it. The "instead" scenario is far more of an edge case imo.
A safer answer may be just to have the application run another query once it returns from this one. As long as it's done on the back end, the performance penalty should be worth the simplicity of managing the development in groups of people, and it's closer in accordance to standards than some crazy feature with edge cases. I'd rather the edge cases be in my code, and avoid them on the platforms. just my opinion don't freak out :)
|
42

Entity Framework performs something similar to gbn's answer:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

The output results are stored in a temporary table variable, and then selected back to the client. Have to be aware of the gotcha:

inserts can generate more than one row, so the variable can hold more than one row, so you can be returned more than one ID

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.

SQL Server 2008 or newer only. If it's 2005 then you're out of luck.

2 Comments

The reason EF does it is to make sure it can also "see" all other changes to the inserted Customer record, as there may be other DB-side logic affecting it, e.g. DEFAULT on some columns, triggers on the table, etc. EF updates the entity (object) it used for the insert, so the client side gets the customer object with the ID and everything else representing the current state of the row.
Another reason not to use EF.
24

There are many ways to exit after insert

When you insert data into a table, you can use the OUTPUT clause to return a copy of the data that’s been inserted into the table. The OUTPUT clause takes two basic forms: OUTPUT and OUTPUT INTO. Use the OUTPUT form if you want to return the data to the calling application. Use the OUTPUT INTO form if you want to return the data to a table or a table variable.

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT: It returns the last identity created for a particular table or view in any session.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY: It returns the last identity from a same session and the same scope. A scope is a stored procedure/trigger etc.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@IDENTITY: It returns the last identity from the same session.

SELECT @@IDENTITY AS [@@IDENTITY];

1 Comment

@RezaJenabi Jun , out put is very good worked, is better than find many id in table. I used out put for bulk insert and insert by select statement . thanks your suggestion
11

@@IDENTITY Is a system function that returns the last-inserted identity value.

1 Comment

Have to advise against ever using @@IDENTITY - it's not accurate (too broad) much less thread-safe -- please see @Curt's answer about SCOPE_IDENTITY().
10

There are multiple ways to get the last inserted ID after insert command.

  1. @@IDENTITY : It returns the last Identity value generated on a Connection in current session, regardless of Table and the scope of statement that produced the value
  2. SCOPE_IDENTITY(): It returns the last identity value generated by the insert statement in the current scope in the current connection regardless of the table.
  3. IDENT_CURRENT(‘TABLENAME’) : It returns the last identity value generated on the specified table regardless of Any connection, session or scope. IDENT_CURRENT is not limited by scope and session; it is limited to a specified table.

Now it seems more difficult to decide which one will be exact match for my requirement.

I mostly prefer SCOPE_IDENTITY().

If you use select SCOPE_IDENTITY() along with TableName in insert statement, you will get the exact result as per your expectation.

Source : CodoBee

Comments

7

The best and most sure solution is using SCOPE_IDENTITY().

Just you have to get the scope identity after every insert and save it in a variable because you can call two insert in the same scope.

ident_current and @@identity may be they work but they are not safe scope. You can have issues in a big application

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

More detail is here Microsoft docs

3 Comments

Can simplify this to select @duplicataId = SCOPE_IDENTITY()
OUTPUT clause is a better, more pure solution :)
OUTPUT INTO is very slow.
4

You can use scope_identity() to select the ID of the row you just inserted into a variable then just select whatever columns you want from that table where the id = the identity you got from scope_identity()

See here for the MSDN info http://msdn.microsoft.com/en-us/library/ms190315.aspx

Comments

4

Recommend to use SCOPE_IDENTITY() to get the new ID value, But NOT use "OUTPUT Inserted.ID"

If the insert statement throw exception, I except it throw it directly. But "OUTPUT Inserted.ID" will return 0, which maybe not as expected.

1 Comment

if an exception is thrown you would assume the statement failed, and any resulting values should be discarded...
1

The confirmed answer is correct for MS SQL Servers Transact-SQL, but if you'd want to do it for PostgreSQL, you would need to use the RETURNING clause:

INSERT INTO table (name)
VALUES('bob')
RETURNING id;

1 Comment

The question is tagged [sql-server], meaning it is specific to Microsoft SQL Server. If you wish to ask (and perhaps answer) a question tagged [postgresql], that would be appropriate.
0

You can append a select statement to your insert statement.

Integer myInt = 
Insert into table1 (FName) values('Fred'); Select Scope_Identity();

This will return a value of the identity when executed scalar.

1 Comment

What does this add that an answer from 7 years prior with the exact same code doesn't provide?
0

This is how I use OUTPUT INSERTED, when inserting to a table that uses ID as identity column in SQL Server:

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)

Comments

-5

After doing an insert into a table with an identity column, you can reference @@IDENTITY to get the value: http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

1 Comment

Never use @@IDENTITY: it isn't scope safe: triggers etc an affect it.
-5

* Parameter order in the connection string is sometimes important. * The Provider parameter's location can break the recordset cursor after adding a row. We saw this behavior with the SQLOLEDB provider.

After a row is added, the row fields are not available, UNLESS the Provider is specified as the first parameter in the connection string. When the provider is anywhere in the connection string except as the first parameter, the newly inserted row fields are not available. When we moved the the Provider to the first parameter, the row fields magically appeared.

4 Comments

Could you tell us how this comment answers / is relevant to the question that was asked? I don't feel it deserves caps/bold. If your answer is deemed helpful, users will vote it up.
A lot of users probably came to this page because they didn't have valid fields to identify the row just added. This behavior we found (that simply changing the order of parameters in the connection string allows accessing the newly added row immediately) is so bizarre that I thought it deserved mentioning in caps, especially since it will very likely fix the reason that people want the new row ID and other fields of that row. By simply putting the provider as the first parameter, the problem disappears.
You need to edit and improve your answer. It's currently noisy and doesn't come across as a decent answer or even an attempt
What exactly do you mean by "noisy"? You need to explain your complaint. It's about as simple as it can be. If you change the order of the parameters in your connection string, it can affect whether row data is available after an insert.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.