How do I return a new IDENTITY column value from an SQLServer SELECT statement? - sql

I'm inserting into an SQLServer table with an autoincrementing key field. (I believe this is called an IDENTITY column in SQLServer.)
In Oracle, I can use the RETURNING keyword to give my INSERT statement a results set like a SELECT query that will return the generated value:
INSERT INTO table
(foreign_key1, value)
VALUES
(9, 'text')
RETURNING key_field INTO :var;
How do I accomplish this in SQLServer?
Bonus: Okay, nice answers so far, but how do I put it into a single statement, if possible? :)

In general, it can't be done in a single statement.
But the SELECT SCOPE_IDENTITY() can (and should) be placed directly after the INSERT statement, so it's all done in the same database call.
Example:
mydb.ExecuteSql("INSERT INTO table(foreign_key1, value) VALUES(9, 'text'); SELECT SCOPE_IDENTITY();");
You can use OUTPUT, but it has some limitations you should be aware of:
http://msdn.microsoft.com/en-us/library/ms177564.aspx

SELECT SCOPE_IDENTITY()
Edit: Having a play...
If only the OUTPUT clause supported local variables.
Anyway, to get a range of IDs rather than a singleton
DECLARE #Mytable TABLE (keycol int IDENTITY (1, 1), valuecol varchar(50))
INSERT #Mytable (valuecol)
OUTPUT Inserted.keycol
SELECT 'harry'
UNION ALL
SELECT 'dick'
UNION ALL
SELECT 'tom'
Edit 2: In one call. I've never had occasion to use this construct.
DECLARE #Mytable TABLE (keycol int IDENTITY (1, 1), valuecol varchar(50))
INSERT #Mytable (valuecol)
OUTPUT Inserted.keycol
VALUES('foobar')

In addition to ##IDENTITY, you should also look into SCOPE_IDENTITY() and IDENT_CURRENT(). You most likely want SCOPE_IDENTITY(). ##IDENTITY has a problem in that it might return an identity value created in a trigger on the actual table that you're trying to track.
Also, these are single-value functions. I don't know how the Oracle RETURNING keyword works.

SCOPE_IDENTITY

It depends on your calling context.
If you're calling this from client code, you can use OUTPUT and then read the value returned.
DECLARE #t TABLE (ColID int IDENTITY, ColStr varchar(20))
INSERT INTO #t (ColStr)
OUTPUT Inserted.ColID
VALUES ('Hello World')
Result:
ColID
-----------
1
If you're wrapping this in a stored procedure, using OUTPUT is more work. There, you'll want to use SCOPE_IDENTITY(), but you can't do it in a single statement. Sure, you can put multiple statements on a single line with a ';' separator, but that's not a single statement.
DECLARE #idValue int
DECLARE #t TABLE (ColID int IDENTITY, ColStr varchar(20))
INSERT INTO #t (ColStr) VALUES ('Hello World')
SELECT #idValue = SCOPE_IDENTITY()
Result: #idValue variable contains identity value. Use an OUTPUT parameter to return the value.

You can use OUTPUT INTO, which has the additional benefits of being able to capture multiple identities inserted.

INSERT INTO table(foreign_key1, value)VALUES(9, 'text');SELECT ##IDENTITY;

Related

sql query to create a table at runtime and insert the values in it from the select statement from the database

what i am tryin to do is make a table(#tbl) runtime and insert the data from the select statement from the database,as what i have done so far is
declare #tbl TABLE (
Item int
)
begin
insert into #tbl values select cid from tbl_custumer where cus_ph like '%'+'987'+'%'
select * from #tbl
end
as "select cid" statement returns multiple records
I think you might want the code to look like this:
begin
declare #tbl TABLE (
Item int
);
insert into #tbl(Item)
select cid
from tbl_custumer
where cus_ph like '%'+'987'+'%';
select *
from #tbl;
end;
Notes:
The begin/end block is not really necessary, but I'm guessing you want it for other reasons (a stored procedure, if, or something similar).
The values keyword is not needed when using insert . . . select.
Use semicolons at the end of each SQL statement. Although they are optional, they make the code easier to follow.

Select row just inserted without using IDENTITY column in SQL Server 2012

I have a bigint PK column which is NOT an identity column, because I create the number in a function using different numbers. Anyway, I am trying to save this bigint number in a parameter #InvID, then use this parameter later in the procedure.
ScopeIdentity() is not working for me, it saved Null to #InvID, I think because the column is not an identity column. Is there anyway to select the record that was just inserted by the procedure without adding an extra ID column to the table?
It would save me a lot of effort and work if there is a direct way to select this record and not adding an id column.
insert into Lab_Invoice(iID, iDate, iTotal, iIsPaid, iSource, iCreator, iShiftID, iBalanceAfter, iFileNo, iType)
values (dbo.Get_RI_ID('True'), GETDATE(),
(select FilePrice from LabSettings), 'False', #source, #user, #shiftID, #b, #fid, 'Open File Invoice');
set #invID = CAST(scope_identity() AS bigint);
P.S. dbo.Get_RI_ID('True') a function returns a bigint.
Why don't you use?
set #invId=dbo.Get_RI_ID('True');
insert into Lab_Invoice(iID,iDate,iTotal,iIsPaid,iSource,iCreator,iShiftID,iBalanceAfter,iFileNo,iType)
values(#invId,GETDATE(),(select FilePrice from LabSettings),'False',#source,#user,#shiftID,#b,#fid,'Open File Invoice');
You already know that big id value. Get it before your insert statement then use it later.
one way to get inserted statement value..it is not clear which value you are trying to get,so created some example with dummy data
create table #test
(
id int
)
declare #id table
(
id int
)
insert into #test
output inserted.id into #id
select 1
select #invID=id from #id

Inserting "bad" data into a SQL database?

I'm writing a query that inserts customer data into a MSSQL database. Very basic.
Unfortunately, I ran into a problem when trying to do the following:
INSERT INTO USERS(newid(),'BOB''S SELECT MARKETING')
I made sure to escape my quotes, but the server is still seeing SELECT as a reserved keyword. I don't want to have to wrap a bunch of reserved words in brackets. Is there a cleaner way of getting my data in the database intact and not mangled by brackets?
I appreciate your help.
Thank you!
You are missing the Key word VALUES:
INSERT INTO USERS VALUES (NEWID(),'BOB''S SELECT MARKETING');
You have several choices of syntax here. Using the one in your code sample, you forgot the VALUES keyword. For example:
declare #users table
(
id uniqueidentifier,
name varchar(50)
)
insert into #users values (newid(), 'BOB''S SELECT MARKETING')
You can also use the insert into / select statement like below if you are inserting a value into each one of the table's columns:
declare #users table
(
id uniqueidentifier,
name varchar(50)
)
insert into #users
select newid(), 'BOB''S SELECT MARKETING'
Or you can use the insert into / select statement and specify the columns you are inserting:
declare #users table
(
id uniqueidentifier,
name varchar(50)
)
insert into #users (id, name)
select newid(), 'BOB''S SELECT MARKETING'

Double columns in a SQL View

We have a legacy interface that inserts into table T1 that values "BODY_TEXT" (varcharmax), "BODY_BIN"(varbinarymax).
It currently inserts just to one of the columns, and leave the other one NULL.
Now we implemented a new interface - table T2 that has only "BODY"(varbinarymax) column.
I need to create a view V1 that should replace T1, meaning
CREATE VIEW V1 AS
SELECT
T2.UNIQUE_ID AS UNIQUE_ID,
etc…
Now I don't know how to treat T2.BODY column… I need to do something like
T2.BODY AS (whatever is not null(BODY_BIN, BODY_TEXT)). It must also support varcharmax vs. varbinarymax.
I tried implementing COALESCE meaning T2.BODY AS COALESCE(BODY_BIN, BODY_TEXT) but it doesn't work.
Nor does
COALESCE(BODY_BIN, BODY_TEXT) AS BODY
T2.BODY AS BODY
Again - In the legacy table we had T1 with two columns - BODY_BIN and BODY_TEXT. The user inserted one value and left the other one null, since body is either binary or textual but not both. The new interface has a table T2 that has only one column, BODY (varbinarymax), and I was asked to delete table T1 and create a view with the same name. Meaning in order to preserve backward comparability they should still be able to perform "insert into T1 values X,Y" (X is DATA_BIN or NULL, and Y is DATA_TEXT or NULL), but the content (taken from either X or Y) should be translated into ONE column in the T2 table - BODY.
I have no idea how to pull this one up.
Can you help me?
Thanks,
Nili
varbinary to varchar (note the order) will cast implicitly. So this works because ISNULL takes the first datatype
ISNULL(varchar, varbinary)
COALESCE fails because it takes the highest precedence datatype (which is varbinary). The implicit cast is not allowed. ISNULL(varbinary, varchar) would fail too
You need an explicit CAST
DECLARE #foo TABLE (ID int IDENTITY (1,1), charmax varchar(MAX) NULL, binmax varbinary(MAX) NULL)
INSERT #foo (charmax, binmax) VALUES ('text', NULL)
INSERT #foo (charmax, binmax) VALUES (NULL, 0x303131)
INSERT #foo (charmax, binmax) VALUES ('Moretext', NULL)
INSERT #foo (charmax, binmax) VALUES (NULL, 0x414243454647)
SELECT ISNULL(binmax, CONVERT(varbinary(MAX), charmax))
FROM #foo
or
SELECT COALESCE(binmax, CONVERT(varbinary(MAX), charmax))
FROM #foo
Edit: I understand the question now... maybe
DECLARE #foo2 TABLE (ID int IDENTITY (1,1), BODY varbinary(MAX) NULL)
INSERT #foo2 (BODY) VALUES (CAST('text' AS varbinary(MAX)))
INSERT #foo2 (BODY) VALUES (0x303132)
INSERT #foo2 (BODY) VALUES (CAST('Moretext' AS varbinary(MAX)))
INSERT #foo2 (BODY) VALUES (0x414243454647)
SELECT
BODY AS BODY_BIN,
CAST(BODY AS varchar(MAX)) AS BOY_TEXT
FROM
#foo2
Edit2: something like this (not tested) to maintain the same write interface. Normally, I'd only maintain a read interface hence the confusion...
CREATE VIEW OldFoo
AS
SELECT
ID,
BODY AS BODY_BIN,
CAST(BODY AS varchar(MAX)) AS BOY_TEXT
FROM
newFoo
GO
CREATE TRIGGER ON OldFoo INSTEAD OF INSERT
AS
SET NOCOUNT ON
INSERT newFoo (BODY)
SELECT ISNULL(binmax, CONVERT(varbinary(MAX), charmax))
FROM INSERTED
GO
First, this is a bad design. Joining on a varchar(max) or varbinary(max) field is a bad idea since they can't be indexed. Prepare for table scans!
You have inconsistent data types in the same column, which is a problem.
Try:
CAST((COALESCE(BODY_BIN, BODY_TEXT)) as varchar(max))

Is there any way to retrieve inserted rows of a command

We probably all know SCOPE_IDENTITY() to retrieve the identity generated by a single insert. Currently I'm in the need of some kind of magic variable or function to retrieve all the rows generated by a statement, eg:
INSERT INTO [dbo].[myMagicTable]
(
[name]
)
SELECT [name]
FROM [dbo].[myMagicSource]
WHERE /* some weird where-clauses with several subselects ... */;
INSERT INTO [dbo].[myMagicBackupTable]
(
[id],
[name]
)
SELECT
[id],
[name]
FROM ???
An insert trigger is no option, as this will perform a single insert which is a problem for a batch of 10.000 rows...
So, is there any way to achieve this?
We are using mssql2005<
For SQL Server 2005+, you can use the OUTPUT clause.
DECLARE #InsertedIDs table(ID int);
INSERT INTO [dbo].[myMagicTable]
OUTPUT INSERTED.ID
INTO #InsertedIDs
SELECT ...
You could define a temporary table (possibly a table variable) and make use of the OUTPUT clause on your INSERT (you can make use of the Inserted pseudo-table, like in a trigger):
DECLARE #NewIDs TABLE (MagicID INT, Name VARCHAR(50))
INSERT INTO [dbo].[myMagicTable]([name])
OUTPUT Inserted.MagicID, Inserted.Name INTO #NewIDs(MagicID, Name)
SELECT [name]
FROM [dbo].[myMagicSource]
WHERE /
and then use that table variable after the INSERT:
INSERT INTO
[dbo].[myMagicBackupTable]([id], [name])
SELECT MagicID, [name]
FROM #NewIDs
and go from there.