Dependent insert statements - sql

I have a table with data about a customer, Customer(name, address), with rows like "John Doe", "Some Street 123". For each row in the table, I want to insert one row in the Person(id, name) table and also one row in the Address(id, person_id, address) table.
I can accomplish this by running two insert statements for each row in Customer:
insert into Person(name) values (#name);
insert into Address(person_id, address) values (scope_identity(), #address);
But this is inefficient. I want to do the inserts in a batch, kind of like this:
-- This works, the problem is with the Address table...
insert into Person(name)
select name from Customer
-- This looks good but does not work because name is not unique.
insert into Address(person_id, address)
select p.person_id, c.address
from Customer c join Person p on c.name = p.name

Leaving this here for the fellow Google traveler that finds this post like me.
I found this solution, and it seems to work great, and doesn't require any funky schema alterations:
https://dba.stackexchange.com/questions/160210/splitting-data-into-two-tables-in-one-go
They use a MERGE statement to perform the initial insert into the first table (the table that is generating the identity to be used everywhere else). The reason it uses the MERGE statement is because it allows you to use an OUTPUT statement, which you can use to output both the new identity value as well as the identity value from the source table (as opposed to using an OUTPUT statement on a standard INSERT which does not allow you to output the source tables identity). You can insert this output data into a mapping table, and use that mapping table to perform the second insert.
Here's my sample code for the solution:
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Set up sample schema and data
------------------------------------------------------------------------------
--Source Data
IF OBJECT_ID('dbo.tmp1') IS NOT NULL DROP TABLE dbo.tmp1 --SELECT * FROM dbo.tmp1
CREATE TABLE dbo.tmp1 (tmp1ID INT IDENTITY(1,1), Col1 CHAR(1) NOT NULL, Col2 CHAR(1) NOT NULL, Col3 CHAR(1) NOT NULL, Col4 CHAR(1) NOT NULL, Col5 CHAR(1) NOT NULL, Col6 CHAR(1) NOT NULL)
INSERT INTO dbo.tmp1 (Col1, Col2, Col3, Col4, Col5, Col6)
SELECT x.c1, x.c2, x.c3, x.c4, x.c5, x.c6
FROM (VALUES ('A','B','C','D','E','F'),
('G','H','I','J','K','L'),
('M','N','O','P','Q','R')
) x(c1,c2,c3,c4,c5,c6)
IF OBJECT_ID('dbo.tmp3') IS NOT NULL DROP TABLE dbo.tmp3 --SELECT * FROM dbo.tmp3
IF OBJECT_ID('dbo.tmp2') IS NOT NULL DROP TABLE dbo.tmp2 --SELECT * FROM dbo.tmp2
--Taget tables to split into
CREATE TABLE dbo.tmp2 (
tmp2ID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_tmp2 PRIMARY KEY CLUSTERED (tmp2ID ASC)
, Col1 CHAR(1) NOT NULL
, Col2 CHAR(1) NOT NULL
, Col3 CHAR(1) NOT NULL
)
CREATE TABLE dbo.tmp3 (
tmp2ID INT NOT NULL
, Col4 CHAR(1) NOT NULL
, Col5 CHAR(1) NOT NULL
, Col6 CHAR(1) NOT NULL
, CONSTRAINT FK_tmp3_tmp2ID FOREIGN KEY(tmp2ID) REFERENCES dbo.tmp2 (tmp2ID)
)
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Split data into two tables
------------------------------------------------------------------------------
DECLARE #Mapping TABLE (tmp1ID INT NOT NULL, tmp2ID INT NOT NULL);
--Use merge statment to output the source data PK as well as the newly inserted identity to generate a mapping table
MERGE INTO dbo.tmp2 AS tgt
USING dbo.tmp1 AS src ON (1=0)
WHEN NOT MATCHED THEN
INSERT ( Col1, Col2, Col3)
VALUES (src.Col1, src.Col2, src.Col3)
OUTPUT src.tmp1ID, Inserted.tmp2ID INTO #Mapping (tmp1ID, tmp2ID);
--Use the mapping table to insert the split data into the second table
INSERT INTO dbo.tmp3 (tmp2ID, Col4, Col5, Col6)
SELECT t2.tmp2ID, t1.Col4, t1.Col5, t1.Col6
FROM dbo.tmp2 t2
JOIN #Mapping m ON m.tmp2ID = t2.tmp2ID
JOIN dbo.tmp1 t1 ON t1.tmp1ID = m.tmp1ID
SELECT tmp2ID, Col1, Col2, Col3 FROM dbo.tmp2
SELECT tmp2ID, Col4, Col5, Col6 FROM dbo.tmp3
------------------------------------------------------------------------------
------------------------------------------------------------------------------
-- Clean up
------------------------------------------------------------------------------
DROP TABLE dbo.tmp1
DROP TABLE dbo.tmp3
DROP TABLE dbo.tmp2
------------------------------------------------------------------------------
------------------------------------------------------------------------------
GO

there is no way to do this as you explain because you lost scope_identity() value of each row of first insert.
A work around may be add Customer primary key fields to Person table and then make join of second insert with this fields:
before insert create customerID field on Person
alter table Person add customerID int null;
then bulk inserts:
-- inserting customerID
insert into Person(name, customerID)
select name, customerID from Customer
-- joining on customerID.
insert into Address(person_id, address)
select p.person_id, c.address
from Customer c
join Person p on c.customerID = p.customerID
after that you can remove customerID field from Person table:
alter table Person drop column customerID

It's better that you create some field of unique types in both table are related them.otherwise you want join as you dont have unique field for condition

Related

How to select more columns than insert column

I have the following query:
INSERT INTO TableA (Col1, Col2, Col3)
OUTPUT #SomeData, INSERTED.ID, ID INTO TableB(SomeColumn, TableAID, ID)
SELECT Col1, Col2, Col3, ID
FROM TableC;
When I run it, I get this error:
The select list for the INSERT statement contains more items than the insert list. The number of SELECT values must match the number of INSERT columns.
That error makes sense, but I don't know how to fix it. I want to select 4 columns from TableC, but I want to only insert three of them (Col1, Col2, Col3) into TableA. I am selecting the column ID because I want to insert it into the ID column of TableB. Is there any way to do that?
CREATE TABLE TableA
(
ID bigint identity
constraint PK_TableA_ID
primary key
Col1 int,
Col2 int,
Col3 int
)
CREATE TABLE TableB
(
ID [int] NOT NULL,
SomeColumn [int] NOT NULL,
TableAID [bigint] NOT NULL
);
CREATE TABLE TableC
(
ID [int] IDENTITY (1,1) NOT NULL,
Col1 [int],
Col2 [int],
Col3 [int],
);
You can try merge and write something like this:
MERGE TableA AS target
USING TableC AS source
ON 1 = 0
WHEN NOT MATCHED BY target
THEN INSERT (Col1, Col2, Col3) VALUES (source.Col1, source.Col2, source.Col3)
OUTPUT #SomeData, INSERTED.ID, source.ID INTO TableB (SomeColumn,TableAID, ID);

Inserting records in one column as different rows in table1 from different columns in one row of table2 in sql-server

I have below tables:
table1 #TempImagepath
column1 Path nvarchare(800)
table2 SiteImage
column1 SiteID bigint,
column2 Facebookurl nvarchare(800),
column3 Twitterurl nvarchare(800),
column4 Instaurl nvarchare(800)
I want to insert data from table2 as different rows into table1 for (Facebookurl,Twitterurl,Instaurl) Where SiteID='10'
Lets say there is one record in table2(SiteImage) as:
(10,"/uploads/Sites/1/CategoryImages/WebImages/7ec79e1a-92c2-4d7c-9139-6d177004d766-201701311804409066.jpg","/uploads/Sites/1/CategoryImages/MobileImages/e5ae525f-7dcf-4051-8463-6bb15f520860-201701311804425434.jpg","/uploads/Sites/1/CategoryImages/MobileImages/31d89a5e-5593-4074-881f-d3326b5cf105-201701311804444181.jpg")
Then my result shoul give records for table1(#TempImagepath) Something Like:
"/uploads/Sites/1/CategoryImages/WebImages/7ec79e1a-92c2-4d7c-9139-6d177004d766-201701311804409066.jpg"
"/uploads/Sites/1/CategoryImages/MobileImages/e5ae525f-7dcf-4051-8463-6bb15f520860-201701311804425434.jpg"
"/uploads/Sites/1/CategoryImages/MobileImages/31d89a5e-5593-4074-881f-d3326b5cf105-201701311804444181.jpg"
Try this,i think this might be useful to you
IF OBJECT_ID('Tempdb..#TempImagepath')IS NOT NULL
DROP TABLE #TempImagepath
IF OBJECT_ID('dbo.SiteImage')IS NOT NULL
DROP TABLE SiteImage
CREATE TABLE #TempImagepath
([Path] nvarchar(800))
CREATE TABLE SiteImage
(
SiteID bigint IDENTITY,
Facebookurl nvarchar(800),
Twitterurl nvarchar(800),
Instaurl nvarchar(800)
)
INSERT INTO SiteImage
SELECT 'Facebookurl','Twitterurl','Instaur'
INSERT INTO #TempImagepath
SELECT 'Row'+ CAST(ROW_NUMBER()OVER(ORDER BY (SELECT 1))AS Varchar(10))+': '+ [Path]
FROM SiteImage
CROSS APPLY (VALUES (Facebookurl),(Twitterurl),(Instaurl)
)AS A ([Path])
SELECT * FROM #TempImagepath
Result
Path
------------------
Row1: Facebookurl
Row2: Twitterurl
Row3: Instaur

Fill automatic data in second table when insert data in one table

I have two table Table1 and Table2 and structure of table as
Table1
Primary Key | Name
Table2
Primary Key | Table1_Id_pk | Status - true/false value
When I insert data into Table2 I want to automatically transfer that data in table1 from Table2 which have Status false.
You can create AFTER INSERT trigger:
CREATE TRIGGER DataMigration
ON Table2
AFTER INSERT
AS
BEGIN
INSERT INTO Table1
SELECT *
FROM TAble2
WHERe Status = 'false'
END
GO
Check this example, use ##identity to get inserted row-id and use that id for child table.
declare #t1 table (pk int identity(1,1) not null , name varchar(50) )
declare #t2 table (pk int identity(1,1) not null , t1pk int, status bit )
declare #new_table1Id int
insert into #t1 values( 'ajay')
set #new_table1Id = ##IDENTITY -This gives you last inserted id of #t1
insert into #t2 values( #new_table1Id , 0)
select * from #t1
select * from #t2
##IDENTITY official documentation

How do I update a table based off the generated index key of an insert?

I'm created a temp table with most of the values I need to insert into a set of tables. From this temp table I have all the values I need for the insert to the first table, but the insert to the next table depends on the identity key generated by the insert to the first table.
I could very well just update my temp table after the first insert, but I'd like to try using the output clause.
I want something like this:
INSERT INTO Table1
<values from temp table>
OUTPUT <update my temp table with generated identity keys>
INSERT INTO Table2
<values from temp table including the output updated id column>
I think you better create another temp table (OR) table type variable and go from there as shown below. Cause I don't think you can update the same temp table from where you are inserting using output clause.
CREATE TABLE TestTable (ID INT not null identity primary key,
TEXTVal VARCHAR(100))
create TABLE #tmp(ID INT, TEXTVal VARCHAR(100))
create TABLE #tmp1(ID INT, TEXTVal VARCHAR(100))
CREATE TABLE TestTable1 (ID INT not null, TEXTVal VARCHAR(100))
INSERT #tmp (ID, TEXTVal)
VALUES (1,'FirstVal')
INSERT #tmp (ID, TEXTVal)
VALUES (2,'SecondVal')
INSERT INTO TestTable (TEXTVal)
OUTPUT Inserted.ID, Inserted.TEXTVal INTO #tmp1
select TEXTVal from #tmp
INSERT INTO TestTable1 (ID, TEXTVal)
select ID, TEXTVal from #tmp1
You could merge your temptable into Table1, and output the results to a variable table, then insert the original data joined to the variable table into Table2.
Example:
DECLARE #MyIDs TABLE (TempTableID int NOT NULL, Table1ID int NOT NULL)
MERGE INTO Table1
USING TempTable AS Tmp
ON Table1.SomeValue = Tmp.SomeValue
WHEN NOT MATCHED THEN
INSERT (col1, col2, col3, col4, col5)
VALUES (tmp.col1, tmp.col2, tmp.col3, tmp.col4, tmp.col5)
OUTPUT Tmp.ID
,Table1.ID
INTO #MyIDs;
INSERT INTO Table2 (col1, col2, col3, col4, col5, Table1ID)
SELECT tmp.col1, tmp.col2, tmp.col3, tmp.col4, tmp.col5, new.Table1ID
FROM TempTable tmp
JOIN #MyIDs new ON tmp.ID = new.TempTableID

SQL - Copy Data Within Same Table

I'm not that great with SQL Server, but I'm trying to do some behind the scenes work to create some functionality that our EMR system lacks - copying forms (and all their data) between patients.
In SQL Server 2008 R2 I have three tables that deal with these forms that have been filled out:
**Table 1**
encounter_id patient_id date time etc etc etc etc
1234 112233 2014-01-02 14:25:01:00 a b c d
**Table 2**
encounter_id page recorded_on recorded_by etc etc
1234 1 2014-01-02 134 asdf asdf
1234 2 2014-01-02 134 jkl; jkl;
**Table 3**
encounter_id page keyname keyvalue
1234 1 key1 aaa
1234 1 key2 bbb
1234 1 key3 ccc
1234 1 key4 ddd
1234 2 key5 eee
1234 2 key6 fff
1234 2 key7 ggg
As you can see, they all match together with the encounter_id, which is linked to the patient_id (In the first table).
What I'm trying to be able to do is copy all the rows in these three tables for a particular encounter_id back into the same table they come from, but with a different (system generated) encounter_id for a patient_id that I would specify. In essence, copying the form from one patient to another.
Any help on this is greatly appreciated.
I always like creating sample tables in [tempdb] so that the syntax is correct. I created tables [t1], [t2], and [t3]. There are primary and foreign keys.
If you have a well developed schema, ERD (entity relationship diagram) http://en.wikipedia.org/wiki/Entity-relationship_diagram , these relationships should be in place.
-- Playing around
use tempdb
go
--
-- Table 1
--
-- Remove if it exists
if object_id('t1') > 0
drop table t1
go
-- Create the first table
create table t1
(
encounter_id int,
patient_id int,
the_date date,
the_time time,
constraint pk_t1 primary key (encounter_id)
);
go
-- Add one row
insert into t1 values (1234, 112233, '2014-01-02', '14:25:01:00');
go
-- Show the data
select * from t1
go
--
-- Table 2
--
-- Remove if it exists
if object_id('t2') > 0
drop table t2
go
-- Create the second table
create table t2
(
encounter_id int,
the_page int,
recorded_on date,
recorded_by int,
constraint pk_t2 primary key (encounter_id, the_page)
);
go
-- Add two rows
insert into t2 values
(1234, 1, '2014-01-02', 134),
(1234, 2, '2014-01-02', 134);
go
-- Show the data
select * from t2
go
--
-- Table 3
--
-- Remove if it exists
if object_id('t3') > 0
drop table t3
go
-- Create the third table
create table t3
(
encounter_id int,
the_page int,
key_name1 varchar(16),
key_value1 varchar(16),
constraint pk_t3 primary key (encounter_id, the_page, key_name1)
);
go
-- Add seven rows
insert into t3 values
(1234, 1, 'key1', 'aaa'),
(1234, 1, 'key2', 'bbb'),
(1234, 1, 'key3', 'ccc'),
(1234, 1, 'key4', 'ddd'),
(1234, 2, 'key5', 'eee'),
(1234, 2, 'key6', 'fff'),
(1234, 2, 'key7', 'ggg');
go
-- Show the data
select * from t3
go
--
-- Foreign Keys
--
alter table t2 with check
add constraint fk_t2 foreign key (encounter_id)
references t1 (encounter_id);
alter table t3 with check
add constraint fk_t3 foreign key (encounter_id, the_page)
references t2 (encounter_id, the_page);
Here comes the fun part, a stored procedure to duplicate the data.
--
-- Procedure to duplicate one record
--
-- Remove if it exists
if object_id('usp_Duplicate_Data') > 0
drop procedure t1
go
-- Create the procedure
create procedure usp_Duplicate_Data #OldId int, #NewId int
as
begin
-- Duplicate table 1's data
insert into t1
select
#NewId,
patient_id,
the_date,
the_time
from t1
where encounter_id = #OldId;
-- Duplicate table 2's data
insert into t2
select
#NewId,
the_page,
recorded_on,
recorded_by
from t2
where encounter_id = #OldId;
-- Duplicate table 3's data
insert into t3
select
#NewId,
the_page,
key_name1,
key_value1
from t3
where encounter_id = #OldId;
end
Last but not least, we have to call the stored procedure to make sure it works.
-- Sample call
exec usp_Duplicate_Data 1234, 7777
In summary, I did not add any error checking or accounted for a range of Id's. I leave these tasks for you to learn.
Made a little fiddle as an example, here (link)
The solution is perhaps needlessly complex but it offers a good variety of other useful stuff as well, I just wanted to test how to build that dynamically. The script does print out the commands, making it relatively easy to remove the TSQL and just produce the plain-SQL to do as you wish.
What it does, is that it requires an encounter_id, which it will then use to dynamically fetch the columns (with the assumption that encounter_id is the PK for TABLE_1) to insert a new record in TABLE_1, store the inserted.encounter_id value, and use that value to fetch and copy the matching rows from TABLE_2 and TABLE_3.
Basically, as long as the structure is correct (TABLE_1 PK is encounter_id which is an identity type), you should be able to just change the table names referenced in the script and it should work directly regardless of which types of columns (and how many of them) your particular tables have.
The beef of the script is this:
/* Script begins here */
DECLARE #ENCOUNTER_ID INT, #NEWID INT, #SQL VARCHAR(MAX), #COLUMNS VARCHAR(MAX)
IF OBJECT_ID('tempdb..##NEW_ID') IS NOT NULL
DROP TABLE ##NEW_ID
CREATE TABLE ##NEW_ID (ID INT)
/* !!! SET YOUR DESIRED encounter_id RECORDS TO BE COPIED, HERE !!! */
SET #ENCOUNTER_ID = 1234
IF EXISTS (SELECT TOP 1 1 FROM TABLE_1 WHERE encounter_id = #ENCOUNTER_ID)
BEGIN
SELECT #COLUMNS = COALESCE(#COLUMNS+', ', 'SELECT ')+name
FROM sys.columns WHERE OBJECT_NAME(object_id) = 'TABLE_1' AND name <> 'encounter_id'
SET #COLUMNS = 'INSERT INTO TABLE_1 OUTPUT inserted.encounter_id INTO ##NEW_ID '+#COLUMNS+' FROM TABLE_1 WHERE encounter_id = '+CAST(#ENCOUNTER_ID AS VARCHAR(25))
EXEC(#COLUMNS)
PRINT(#COLUMNS)
SELECT TOP 1 #NEWID = ID, #COLUMNS = NULL FROM ##NEW_ID
SELECT #COLUMNS = COALESCE(#COLUMNS+', ', '')+name
FROM sys.columns WHERE OBJECT_NAME(object_id) = 'TABLE_2'
SET #COLUMNS = 'INSERT INTO TABLE_2 ('+#COLUMNS+') SELECT '+REPLACE(#COLUMNS,'encounter_id',''+CAST(#NEWID AS VARCHAR(25))+'')
+' FROM TABLE_2 WHERE encounter_id = '+CAST(#ENCOUNTER_ID AS VARCHAR(25))
EXEC(#COLUMNS)
PRINT(#COLUMNS)
SET #COLUMNS = NULL
SELECT #COLUMNS = COALESCE(#COLUMNS+', ', '')+name
FROM sys.columns WHERE OBJECT_NAME(object_id) = 'TABLE_3'
SET #COLUMNS = 'INSERT INTO TABLE_3 ('+#COLUMNS+') SELECT '+REPLACE(#COLUMNS,'encounter_id',''+CAST(#NEWID AS VARCHAR(25))+'')
+' FROM TABLE_3 WHERE encounter_id = '+CAST(#ENCOUNTER_ID AS VARCHAR(25))
EXEC(#COLUMNS)
PRINT(#COLUMNS)
IF OBJECT_ID('tempdb..##NEW_ID') IS NOT NULL
DROP TABLE ##NEW_ID
END
declare #oldEncounterID int
set #oldEncounterID = 1234
declare #newEncounterID int
set #newEncounterID = 2345
insert into table1(encounter_id, patient_id, date, time, etc)
select newEncounterID, patient_id, date, time, etc
from table1 where encounter_id = oldEncounterID
and so on... problem with this approach you must know in advantage what all the columns are, and if they change you may change the columns accordingly
Another approach:
declare #oldEncounterID int
set #oldEncounterID = 1234
declare #newEncounterID int
set #newEncounterID = 2345
select * into #table1 from table1 where encounter_id = oldEncounterID
update #table1 set encounter_id = newEncounterID
insert into table1 select * from #table1
and so on... this second approach however may need a little adjustment if there is an identity column then you'll have to set identity_insert to on
Psuedo code, not tested:
DECLARE #patient_id INT, #date datetime, #time ??
SET #patient_id = 112244 --your patient id
INSERT INTO [**Table 1**] (patient_id, date, time, etc, etc, etc, etc)
VALUES (#patient_id, #date, #time, 'a', 'b', 'c', 'd')
DECLARE #encounter_id int
SET #encounter_id = SCOPE_IDENTITY -- or select #encounter_id = encounter_id from [**Table 1**] where patientId = #patient_id
INSERT INTO [**Table 2**] (encounter_id, page, recorded_on, recorded_by, etc, etc2)
SELECT #encounter_id, page, recorded_on, recorded_by, etc, etc2
FROM [**Table 2**]
WHERE encounter_id = 1234
INSERT INTO [**Table 3**] (encounter_id, page, keyname, keyvalue)
SELECT #encounter_id, page, keyname, keyvalue
FROM [**Table 3**]
WHERE encounter_id = 1234