SQL Insert from one TVP into two tables, using scope identity from first for second table - sql

I have SQL TVP object with multiple records (for example 2 records).
I need to insert these records into two almost identical tables, the only difference is that second table has one more column which is foreign key pointing to first table. So it should loop TVP records and insert one by one into both tables, but getting scope_identity() of inserted record in first table and use it for record in second table.
1st iteration
insert into first table
get scope_identity() of inserted record
insert into second table (using scope indentity from first table to fill additional column)
And so on, depending on how many records are in TVP.
How can I achieve this?

Obviously I have left out a ton of code since we don't have your column and table names etc. You want an ID value in your TVP so you can count rows and use it in a where clause and while loop.
Declare #Var1 Int
Declare #YourTVP YourTVPName
Declare #RowCounter Int = 1
While (1=1)
Insert Into YourTable1 (Column1, ...)
Select (Column1, ...)
From #YourTVP
Where #RowCounter = SomeIDColumn
#Var1 = Select ##SCOPE_IDENTITY()
Insert Into YourTable2 (Column1, ...)
(#Var1, ...)
If (Some logic to Break your While loop)
Break
Else #RowCounter = #RowCounter + 1
End

Ok, let me be more clear. I will give demonstrative example::
I have TVP (let name it as PersonTVP) containing FirstName and LastName columns and assume PersonTVP has two records.
I have two tables, Person and PersonExtra. Person table has Id, FirstName and LastName columns, and PersonExtra has same columns + one additional column PersonId.
I need to insert data from PersonTVP into these two tables. Flow should be:
Take record from PersonTVP and insert into Person table
Get Scope_Identity() of inserted record (the value from Id column)
Insert same record into PersonExtra table and use Scope_Identity() for PersonId column (additional column)
And so on, loop as long as PersonTVP has records.

Related

Insert bulk data into two related tables with foreign keys from another table

I have imported some data to a temp SQL table from an Excel file. Then I have tried to insert all rows to two related tables. Simply like this: There are Events and Actors tables with many to many relationship in my database. Actors are already added. I want to add all events to Events table and then add relation(ActorId) for each event to EventActors tables.
(dbo.TempTable has Title, ActorId columns)
insert into dbo.Event (Title)
Select Title
From dbo.TempTable
insert into dbo.EventActor (EventId, ActorId)
Select SCOPE_IDENTITY(), ActorId --SCOPE_IDENTITY() is for EventId
From dbo.TempTable
When this code ran, all events inserted into Events, but the relations didn't inserted into EventActors because of Foreign Key error.
I think there should be a loop. But I am confused. I don't want to write C# code for this. I know there would be a simple but advanced solution trick for this in SQL Server. Thanks for your help.
Use the output clause to capture the new IDs, with a merge statement to allow capture from both source and destination tables.
Having captured this information, join it back to the temp table for the second insert.
Note you need a unique id per row, and this assumes 1 row in the temp table creates 1 row in both the Event and the EventActor tables.
-- Ensure every row has a unique id - could be part of the table create
ALTER TABLE dbo.TempTable ADD id INT IDENTITY(1,1);
-- Create table variable for storing the new IDs in
DECLARE #NewId TABLE (INT id, INT EventId);
-- Use Merge to Insert with Output to allow us to access all tables involves
-- As Insert with Output only allows access to columns in the destination table
MERGE INTO dbo.[Event] AS Target
USING dbo.TempTable AS Source
ON 1 = 0 -- Force an insert regardless
WHEN NOT MATCHED THEN
INSERT (Title)
VALUES (Source.Title)
OUTPUT Source.id, Inserted.EventId
INTO #NewId (id, EventId);
-- Insert using new Ids just created
INSERT INTO dbo.EventActor (EventId, ActorId)
SELECT I.EventId, T.ActorId
FROM dbo.TempTable T
INNER JOIN #NewId I on T.id = T.id;

Insert a record into 2 tables in SQL Server using comma separated value

I have 2 table A and table B; table B is linked to table A through a foreign key.
TABLE A has a structure somewhat like this
PK Id
DeliveryChannelValue
DeliverychannelId
Date time
Table B has this structure
PK Id UniqueIdentifiers
Date time
FK tableA id
Now in a stored procedure, I get unique identifiers as comma separated value, so based on the number of items in that list, I have to create the same number of rows in table A and in table B.
If the number of items in comma separated value is 3, then there will be 3 rows to be inserted into table A and 3 rows into table B. I am trying to avoid a cursor.
Please suggest efficient way to do this.
You can use this CodeProject project split function to separate the values, and then use a known DateTime stamp to keep the tables in sync. This assumes these values aren't constantly updating which could cause a DateTime duplication issue: if that's the case, you'll need to use a add a GUID value in place of the YOURDATE field, below:
DECLARE #DATESTAMP DATETIME = GETDATE()
INSERT INTO TABLE_A (ID, YOURDATE)
SELECT item, #DATESTAMP
FROM dbo.[FN_SPLIT](#yourinputstring)
GO
INSERT INTO TABLE_B(YOURDATE, TABLE_A_ID)
SELECT #DATESTAMP, ID
FROM TABLE_A
WHERE YOURDATE = #DATESTAMP
GO

Multiple row insert into two tables avoiding loops

I have a set of value which have to be inserted into two tables.Input has say 5 row and I have to insert these 5 rows into table A first.Table A has a identity column.Next i have to insert these 5 rows into table B with an extra column which is the identity from table A.
How this can be done with out using any loops?
Any help will be highly helpful.
INSERT INTO TABLE_A(COL2,COL3)
SELECT COL2,COL3 FROM #TEMP_TAB
set #identityval=##identity
INSERT INTO TABLE_B(COLA,COLB,COLC)
SELECT #identityval,COL2,COL3,COL4 FROM #TEMP_TAB
You cannot insert into multiple tables using a single statment.
What you could do is create an insert trigger on Table A so that after the insert occurs this performs the new insert with the identity of the value inserted into Table A and insert it into Table B.
Here is one solution.
take max of identity column from table TABLE_A
insert new records in table TABLE_A
then insert records on TABLE_B from TABLE_A with Identity greater than last max identity.
Thanks,
Gopal
What you want to do is not possible.
You can get only the value from the last insert using the ##identity variable. This way its possible to add to multiple tables setting the right foreign key without selecting the just inserted row again using a cursor. This approach is not useful when inserting multiple rows at once.
From the documentation:
Use the ##identity global variable to retrieve the last value inserted into an IDENTITY column. The value of ##identity changes each time an insert or select into attempts to insert a row into a table.
Here is a procedure which inserts a single row and you can use the return value to create a reference to the inserted data in another table:
create procedure reset_id as
set identity_insert sales_daily on
insert into sales_daily (syb_identity, stor_id)
values (102, "1349")
select ##identity
select ##identity
execute reset_id

Insert into a row at specific position into SQL server table with PK

I want to insert a row into a SQL server table at a specific position. For example my table has 100 rows and I want to insert a new row at position 9. But the ID column which is PK for the table already has a row with ID 9. How can I insert a row at this position so that all the rows after it shift to next position?
Relational tables have no 'position'. As an optimization, an index will sort rows by the specified key, if you wish to insert a row at a specific rank in the key order, insert it with a key that sorts in that rank position. In your case you'll have to update all rows with a value if ID greater than 8 to increment ID with 1, then insert the ID with value 9:
UPDATE TABLE table SET ID += 1 WHERE ID >= 9;
INSERT INTO TABLE (ID, ...) VALUES (9, ...);
Needless to say, there cannot possibly be any sane reason for doing something like that. If you would truly have such a requirement, then you would use a composite key with two (or more) parts. Such a key would allow you to insert subkeys so that it sorts in the desired order. But much more likely your problem can be solved exclusively by specifying a correct ORDER BY, w/o messing with the physical order of the rows.
Another way to look at it is to reconsider what primary key means: the identifier of an entity, which does not change during that entity lifetime. Then your question can be rephrased in a way that makes the fallacy in your question more obvious:
I want to change the content of the entity with ID 9 to some new
value. The old values of the entity 9 should be moved to the content
of entity with ID 10. The old content of entity with ID 10 should be
moved to the entity with ID 11... and so on and so forth. The old
content of the entity with the highest ID should be inserted as a new
entity.
Usually you do not want to use primary keys this way. A better approach would be to create another column called 'position' or similar where you can keep track of your own ordering system.
To perform the shifting you could run a query like this:
UPDATE table SET id = id + 1 WHERE id >= 9
This do not work if your column uses auto_increment functionality.
No, you can't control where the new row is inserted. Actually, you don't need to: use the ORDER BY clause on your SELECT statements to order the results the way you need.
DECLARE #duplicateTable4 TABLE (id int,data VARCHAR(20))
INSERT INTO #duplicateTable4 VALUES (1,'not duplicate row')
INSERT INTO #duplicateTable4 VALUES (2,'duplicate row')
INSERT INTO #duplicateTable4 VALUES (3,'duplicate rows')
INSERT INTO #duplicateTable4 VALUES (4,'second duplicate row')
INSERT INTO #duplicateTable4 VALUES (5,'second duplicat rows')
DECLARE #duplicateTable5 TABLE (id int,data VARCHAR(20))
insert into #duplicateTable5 select *from #duplicateTable4
delete from #duplicateTable4
declare #i int , #cnt int
set #i=1
set #cnt=(select count(*) from #duplicateTable5)
while(#i<=#cnt)
begin
if #i=1
begin
insert into #duplicateTable4(id,data) select 11,'indian'
insert into #duplicateTable4(id,data) select id,data from #duplicateTable5 where id=#i
end
else
insert into #duplicateTable4(id,data) select id,data from #duplicateTable5 where id=#i
set #i=#i+1
end
select *from #duplicateTable4
This kind of violates the purpose of a relational table, but if you need, it's not really that hard to do.
1) use ROW_NUMBER() OVER(ORDER BY NameOfColumnToSort ASC) AS Row to make a column for the row numbers in your table.
2) From here you can copy (using SELECT columnsYouNeed INTO ) the before and after portions of the table into two separate tables (based on which row number you want to insert your values after) using a WHERE Row < ## and Row >= ## statement respectively.
3) Next you drop the original table using DROP TABLE.
4) Then you use a UNION for the before table, the row you want to insert (using a single explicitly defined SELECT statement without anything else), and the after table. By now you have two UNION statements for 3 separate select clauses. Here you can just wrap this in a SELECT INTO FROM clause calling it the name of your original table.
5) Last, you DROP TABLE the two tables you made.
This is similar to how an ALTER TABLE works.
INSERT INTO customers
(customer_id, last_name, first_name)
SELECT employee_number AS customer_id, last_name, first_name
FROM employees
WHERE employee_number < 1003;
FOR MORE REF: https://www.techonthenet.com/sql/insert.php

Basic T-SQL Question

Let's say I have three tables implemented with a many-to-many relationship. Something like, Person(personID), PersonMovies(personID, movieID), and Movies(movieID). What is the correct way to do multiple inserts in sql server? I would like to insert the person, the movies and then be able to get all of the movies a person owns. So would it be three inserts within a transaction? If so, I would assume the easy part is inserting into the person and movie table, but how would I insert into the PersonMovies table, since that table relies on the existing ID's in the other two tables. I'm assuming that I would insert into Person and Movies, then some way set assign the ID's of the newly inserted tables to a variable from those two tables, then use those variables to insert into the bridge table. I have no idea, but I hope this makes some kind of sense as I'm VERY confused by this!!
Begin by inserting the Person record and use SCOPE_IDENTITY to get the unique ID if the inserted record. You can then use this to insert the person's Movies. Before you can insert a persons Movie you need to see whether it exists or not using IF EXISTS. If it does SELECT it from the existing table and assign it's unique ID to a variable. If it doesn't yet exist use the same technique for adding the person and insert the Movie then assign SCOPE_IDENTITY to the movie variable.
In PL/SQL there is an UPSERT statement which combines updating records or inserting them when required. I've added code below for a procedure which does an UPSERT in T/SQL and return the unique ID if a record had to be created.
IF EXISTS (SELECT id FROM dbo.sysobjects WHERE name = 'fts_upsert_team') DROP PROCEDURE fts_upsert_team
GO
CREATE PROCEDURE fts_upsert_team
#teamID INT OUTPUT,
#name VARCHAR(100)
AS
UPDATE
fts_teams
SET
name = #name
WHERE
teamID = #teamID
IF ##ROWCOUNT = 0
BEGIN
INSERT INTO fts_teams
(
name
)
VALUES
(
#name
)
SET #teamID = SCOPE_IDENTITY()
END
GO
I assume that you are having Person and Movies auto increment. If this is the case you need to capture what the key field is after the insert. You can use the scope_identity() function to get the this value. After each insert, save thes to a variable, and then when you isert into PersonMovies, use the saved values.