164

Is there any "simple" way to do this or I need to pass by a table variable with the "OUTPUT ... INTO" syntax?

DECLARE @someInt int

INSERT INTO MyTable2(AIntColumn)
OUTPUT @SomeInt = Inserted.AIntColumn
VALUES(12)
OMG Ponies
  • 325,700
  • 82
  • 523
  • 502
Benoittr
  • 4,091
  • 4
  • 27
  • 38

4 Answers4

204

You need a table variable and it can be this simple.

declare @ID table (ID int)

insert into MyTable2(ID)
output inserted.ID into @ID
values (1)
Mikael Eriksson
  • 136,425
  • 22
  • 210
  • 281
  • 64
    But then I'd have to "SELECT @someInt = ID FROM @ID". I wanted to know if its possible to skip that extra step (and intermediary table variable) if all I need is the resulting int. – Benoittr Apr 05 '11 at 21:33
  • @Benoittr - That depends on how you are going to use the value, it might not be necessary, you could use the table in a from clause of a select statement. When you assign a variable you also need to be sure of that the insert only inserted one row. And if the insert only inserted one row, perhaps it is easier to get hold of what is used in the values clause directly instead of using `output`? – Mikael Eriksson Apr 05 '11 at 21:42
  • 5
    In the case of auto-generated value it is not always possible to know the values ahead of time(identity, computed columns). I understand there are many workaround. Still, you gave me the answer I was looking for. Thanks – Benoittr Apr 06 '11 at 20:56
  • 5
    Need it in a regular variable? `DECLARE @InsertedIDResults TABLE (ID int); INSERT INTO MyTable (Name, Age) OUTPUT INSERTED.ID INTO @InsertedIDResults VALUES('My Name', 30); DECLARE @InsertedID int = (SELECT TOP 1 ID FROM @InsertedIDResults);` – Arvo Bowen Apr 10 '20 at 18:53
37

Over a year later... if what you need is get the auto generated id of a table, you can just

SELECT @ReportOptionId = SCOPE_IDENTITY()

Otherwise, it seems like you are stuck with using a table.

Alejandro B.
  • 4,807
  • 2
  • 33
  • 61
  • 8
    The variable I was looking for really was something else then the identity column. Still, thanks for the answer. – Benoittr Aug 24 '12 at 17:39
  • 29
    Apparently this has issues in a Multi Processor Parallel Plan, and OUTPUT is the only always trustworthy method. – andrewb Mar 10 '14 at 03:18
  • 8
    SCOPE_IDENTITY() might return something even if the *last* INSERT did not insert anything, right ? That would make it unusable in some cases. – iDevlop Jan 28 '15 at 11:07
  • @iDevlop that may be true (I don't really know) but I suppose you can workaround it using IF @@RowCount > 0... I'm really kind of thinking out loud though – Alejandro B. Feb 03 '15 at 16:45
  • It will not work for sequences. In this particular way, Mikael Eriksson answer is better. – EKOlog May 06 '15 at 13:42
  • 3
    better to use output clause – Clay Smith Aug 30 '15 at 01:25
  • 4
    The bug @andrewb refers to is fixed in [2008 R2 SP1](https://support.microsoft.com/en-us/help/2019779/you-may-receive-incorrect-values-when-using-scope-identity-and-identit). – adam0101 Apr 21 '20 at 14:44
12

Way later but still worth mentioning is that you can also use variables to output values in the SET clause of an UPDATE or in the fields of a SELECT;

DECLARE @val1 int;
DECLARE @val2 int;
UPDATE [dbo].[PortalCounters_TEST]
SET @val1 = NextNum, @val2 = NextNum = NextNum + 1
WHERE [Condition] = 'unique value'
SELECT @val1, @val2

In the example above @val1 has the before value and @val2 has the after value although I suspect any changes from a trigger would not be in val2 so you'd have to go with the output table in that case. For anything but the simplest case, I think the output table will be more readable in your code as well.

One place this is very helpful is if you want to turn a column into a comma-separated list;

DECLARE @list varchar(max) = '';
DECLARE @comma varchar(2) = '';
SELECT @list = @list + @comma + County, @comma = ', ' FROM County
print @list
Jay13
  • 860
  • 9
  • 15
  • Thanks! This was the info I needed – Wizou Dec 06 '19 at 10:51
  • 1
    WOW! I didn't know you can you do `SET @val2 = NextNum = NextNum + 1`. – Sam Apr 30 '20 at 10:12
  • 5
    Completely **different** to what was asked, didn't help at all, because I was looking for a way to assign an `OUTPUT` value to a variable in an `INSERT` clause using `INSERT-OUTPUT-VALUES` approach as asked by the asker. – Fawad Raza Aug 04 '20 at 14:04
9

Answer

You can use @@IDENTITY to get the last inserted id.

DECLARE @someInt int
INSERT INTO MyTable2(AIntColumn)
VALUES(12)
SET @someInt = @@IDENTITY;

Assuming your table has a primary key which you are looking to set as said variable.

Example schema

CREATE TABLE MyTable2(
    [ID] [INT] IDENTITY(1,1),
    [aIntColumn] [INT]
)
CREATE TABLE MyTable3(
    [ID] [INT] IDENTITY(1,1),
    [newInt] [INT],
    [FK_int] [INT]
)

Then you can use that in the next part of your script, e.g.

DECLARE @someInt INT;
INSERT INTO MyTable2(AIntColumn)
VALUES(12)
SET @someInt = @@IDENTITY;

--do something else
DECLARE @someInt2 INT;
INSERT INTO MyTable3(newInt, FK_int)
VALUES(101, @someInt)
SET @someInt2 = @@IDENTITY;

SELECT @someInt AS 'First Inserted ID ',  @someInt2 AS 'Second inserted ID';
Simon
  • 496
  • 4
  • 19
  • 1
    this was the simplest and best answer for me. – Praxiom Apr 05 '23 at 15:05
  • Unfortunately @@IDENTITY is not to be trusted. You will sometimes get the last inserted id from a different table. It is extremely difficult to diagnose an error like this in production. Use OUTPUT clause instead – Brennan Pope May 08 '23 at 15:16
  • 1
    Although `@@IDENTITY is not limited to a specific scope.` `@@IDENTITY and SCOPE_IDENTITY return the last identity value generated in any table in the current session.` So your comment entirely depends on the architecture of your product. "Trusted" is unfair to say, but you should definitely appropriate your code with your dataflow. – Simon May 09 '23 at 11:42
  • I don't disagree, this isn't wrong, but I was just trying to say that there are newer and safer ways to get the identity. – Brennan Pope Aug 31 '23 at 16:07