Output insert and table values when doing insert into - sql-server

I'm inserting into a table in MS SQL Server 2008 (it's rather a copy of values from the same table) and want to get the output values for the insert. I want to get the id value of the select statement (t.id in the example below), the INSERTED.id works just fine
create table tmp.tbl_inserted (fromId int, toId int)
INSERT INTO mytable (name)
OUTPUT t.id, INSERTED.id INTO tmp.tbl_inserted
SELECT t.name FROM mytable t
Thanks in advance

You can't do it directly from an INSERT:
from_table_name
Is a column prefix that specifies a table included in the FROM clause of a DELETE, UPDATE, or MERGE statement that is used to specify the rows to update or delete.
Note that INSERT isn't mentioned.
What you have to do instead is cheat and use a MERGE:
MERGE INTO mytable m
USING (name,id FROM mytable) t ON 1=0
WHEN NOT MATCHED THEN INSERT (name) VALUES (t.name)
OUTPUT t.id, INSERTED.id INTO tmp.SizeCurveGroup_inserted
;

Related

How to Auto increment column when using merge and in not matching case - SQL Server

Want to add a incremental number in MERGE when the NOT MATCHED case.
It only allows to write insert statements, I tried to set the value using the MAX() function too, but adds the same number to all the records.
What is expected
MERGE #targetTable AS [target]
USING #sourceTable AS [source] ON [target].key = [source].key
WHEN MATCHED
UPDATE ROWS
WHEN NOT MATCHED THEN
INSERT (id, Name)
VALUES ([incremented_id], source.Name);
Note : Table already have some records in it.
Any help would be appreciated
Try to get max ID from the table and then add it to this parameter. With this select you will be creating a new id of every row that is inserted. Something like this should work
declare #root int = 64895
WHEN MATCHED
UPDATE ROWS
WHEN NOT MATCHED THEN
INSERT (id, Name)
VALUES( ((#root - 1) + ROW_NUMBER() over(order by ID)), source.Name);

SQL Server Maximum rows that can be inserted in a single insert statment

I want to do a batch insert, similar to this question
How to do a batch insert in MySQL
What is the limitation is SQL Server on how many rows can be inserted in a single insert statement ?
What happens when for example the first value is inserted but the second one causes a primary key violation. Are the all INSERT statements rolled back?
INSERT INTO tbl_name (a,b)
VALUES (1, 2), (1, 3));
The Maximum number of rows you can insert in one statement is 1000 when using INSERT INTO ... VALUES... i.e.
INSERT INTO TableName( Colum1)
VALUES (1),
(2),
(3),...... upto 1000 rows.
But if your are using a SELECT statement to insert rows in a table, there is no limit for that, something like...
INSERT INTO TableName (ColName)
Select Col FROM AnotherTable
Now coming to your second question. What happens when an error occurs during an insert.
Well if you are inserting rows using multi-value construct
INSERT INTO TableName( Colum1)
VALUES (1),
(2),
(3)
In the above scenario if any row insert causes an error the whole statement will be rolled back and none of the rows will be inserted.
But if you were inserting rows with a separate statement for each row i.e. ...
INSERT INTO TableName( Colum1) VALUES (1)
INSERT INTO TableName( Colum1) VALUES (2)
INSERT INTO TableName( Colum1) VALUES (3)
In the above case each row insert is a separate statement and if any row insert caused an error only that specific insert statement will be rolled back the rest will be successfully inserted.
You can actually pass in an unlimited number of records using a subquery.
;WITH NewData AS (SELECT * FROM ( VALUES (1, 'A'),(2,'B'),(3,'C')) x (Id, SomeName))
INSERT INTO TableName (Column1, Column2) SELECT Id, SomeName FROM NewData
Although the max is 1000, it's been demonstrated that performance begins to diminish at much smaller numbers. Eugene Philipov wrote a great article exploring this very topic:
https://www.red-gate.com/simple-talk/sql/performance/comparing-multiple-rows-insert-vs-single-row-insert-with-three-data-load-methods/
To summarize, the author did some very well-designed experimenting and found a sweet spot at around 25. YMMV.
Dutchman's solution is cool, but it can be simplified. It turns out that you don't need the CTE. I tried it, and it works without the CTE, like this:
INSERT INTO TableName (Column1, Column2) SELECT Id, SomeName
FROM ( VALUES (1, 'A'),(2,'B'),(3,'C')) x (Id, SomeName)
you can try this
with tempDataTable AS (SELECT *From (VALUES
(18001,79626,'1992-12-11','1993-12-11') -- this is data u want to insert
)x(empNO,sal,frmDate,toDate)) -- tempDataColoumns
INSERT INTO salaries(emp_no,salary,from_date,to_date) SELECT empNO,sal,frmDate,toDate from newData
Remove '--' at the time of query
There's short workaround to avoid rows limit and still treat it like one statement (all goes in or if there's one error, everything is rolled back)
INSERT INTO tbl_name (a,b)
SELECT 1,2 UNION ALL
SELECT 1,3 UNION ALL
SELECT 1,4 .......
This is how I add more than 1000 values to the temp table.
CREATE TABLE #TempTable(ID int)
INSERT INTO #TempTable (ID)
SELECT * from (VALUES (45764),(45763),(45762),(45761),(45760),(45759),(45758),(45757),(45756)....)AS temp (column1)

SQL Merge with inserting into the third table

I want to create a merge that will compare two tables and insert not matched values into another third table or table variable
something like this:
MERGE Assets AS target
USING (#id, #name)FROM Sales AS source (id, name) ON (target.id = SOURCE.id)
WHEN MATCHED THEN
UPDATE SET target.Status = #status, target.DateModified = SYSUTCDATETIME()
WHEN NOT MATCHED THEN
INSERT INTO #tableVar (id, name, status, dateModified)
VALUES (#id, #name, #status, SYSUTCDATETIME())
Can this be done or are there other methods?
You just cannot do this. MERGE operates on two tables only - source and target.
For your requirement, you need to e.g. use a CTE (Common Table Expression) to find the rows that don't match - and insert those into the third table.
Something like:
;WITH NonMatchedData AS
(
-- adapt this as needed - just determine which rows match your criteria,
-- and make sure to return all the columns necessary for the subsequent INSERT
SELECT (columns)
FROM dbo.SourceTable
WHERE ID NOT IN (SELECT DISTINCT ID FROM dbo.TargetTable)
)
INSERT INTO dbo.ThirdTable(Col1, Col2, ....., ColN)
SELECT Col1, Col2, ....., ColN
FROM NonMatchedData
You CAN do this very easily...
You can wrap the MERGE statement within a INSERT INTO FROM:
http://technet.microsoft.com/en-us/library/bb510625.aspx#sectionToggle2
-OR-
You can do it directly within the merge statement:
Quick example:
WHEN NOT MATCHED THEN
DELETE
OUTPUT Deleted.* INTO dbo.MyTable;
This will insert the non-matches into your existing destination table. You can use the Updated, Inserted, Deleted v-tables to direct data other places.

SQL Server: is it possible to get recently inserted identity column value without table variable

Let's say I have a table
my_table(id int identity(1,1) not null primary key, data varchar(100))
I want to write a procedure that inserts a new row into that table and returns id.
I tried
DECLARE #new_id INT;
SELECT #new_id = id FROM
(
INSERT INTO my_table(data) OUTPUT inserted.id VALUES ('test')
) as NewVal(id)
That code doesn't work (I got "A nested INSERT, UPDATE, DELETE, or MERGE statement is not allowed in a SELECT statement that is not the immediate source of rows for an INSERT statement."). However, if I use a table variable, I can do
DECLARE #new_id INT;
DECLARE #tmp_table TABLE(int id);
INSERT INTO #tmp_table
SELECT id FROM
(
INSERT INTO my_table(data) OUTPUT inserted.id VALUES ('test')
) as NewVal(id);
// OR
INSERT INTO my_table(data) OUTPUT inserted.id INTO #tmp_table VALUES ('test') ;
SELECT #new_id = id FROM #tmp_table;
Is it possible to achieve the same functionality without using table variable ?
UPDATE
Thanks for quick responses, +1 to everyone for solution with SCOPE_IDENTITY.
That's probably my fault, I should have asked the question clearly - I do use MERGE (an example would be much longer, so I posted INSERT instead) , not INSERT so SCOPE_IDENTITY doesn't really work for me.
A bit shorter version than nesting in a insert statement is using output...into.
declare #tmp_table table(actiontaken nvarchar(10), id int);
merge my_table
using (values ('test')) as S(data)
on 0=1
when not matched then
insert (data) values (S.data)
output $action, inserted.id into #tmp_table;
I do believe that you should use a table variable from the merge. The output may contain more than one row.
Yes, you can just return SCOPE_IDENTITY after the insert (this is safer than ##IDENTITY due to the scoping differences).
i.e.
INSERT my_table (data) VALUES ('test')
SELECT SCOPE_IDENTITY()
SELECT SCOPE_IDENTITY() will get the last inserted identity value in the scope.
If you use
select ##identity
you will get the last value entered into the identity column from anywhere.
If you want the value entered into the column from the script that just ran, you should use
select SCOPE_IDENTITY()
If you just want the last value inserted into the identity column for a given table from any statement in any session, you should use
select IDENT_CURRENT('tablename')
select ##identity or select SCOPE_IDENTITY() will return the value you're looking for

SQL Server list of insert identities

I have a table with an autoincrement id that I am doing a
INSERT INTO ( ... ) SELECT ... FROM ...
Is there a way for me to get the list of id's that have been inserted?
I was thinking I could get the max id before the insert then after and assuming everything in between is new, but then if a row gets inserted from somewhere else I could run into problems. Is there a proper way to do this?
I am using SQL Server 2005
Use the output clause.
DECLARE #InsertedIDs table(ID int);
INSERT INTO YourTable
OUTPUT INSERTED.ID
INTO #InsertedIDs
SELECT ...
Create a table variable and then use the OUTPUT clause into the table variable.
OUTPUT inserted.NameOfYourColumnId INTO tableVariable
Then you can SELECT from your table variable.

Resources