Copy Rows with PK feedback loop - sql

Given the following (Table1):
Id Field1 Field2 ...
-- ------ -------
NULL 1 2
NULL 3 4
...
I'd like to insert the values of Field1 and Field2 into a different table (Table2). Table2 has an auto increment integer primary key. I want to retrieve the new PKs from Table2 and update the Id column above (Table1).
I realize this is not conventional - its not something I need to do regularly, simply one-off for some migration work. I made some attempts using INSERT INTO, OUTPUT, INSERTED.Id, but failed. The PKs that are "looped-back" into Table1 must tie to the values of Field1/Filed2 inserted.

You should just be able to do a insert, then a delete and re-insert.
create table t1
( id int, f1 int, f2 int);
create table t2
( id int primary key IDENTITY , f1 int, f2 int);
insert into t1 (id, f1, f2) values (null, 1, 2);
insert into t1 (id, f1, f2) values (null, 3, 4);
insert into t1 (id, f1, f2) values (null, 5, 6);
insert into t1 (id, f1, f2) values (null, 5, 6);
insert into t2 (f1, f2)
select f1, f2 from t1 where id is null;
delete t1
from t1 join t2 on (t1.f1 = t2.f1 and t1.f2 = t2.f2);
insert into t1
select id, f1, f2 from t2;
select * from t1;
See this example on SQLFiddle.

You will need some type of unique key to match your rows in each table. I've taken the liberty of adding a TempGuid column to each of your tables (which you can later drop):
-- Setup test data
declare #Table1 table (
Id int null
, Field1 int not null
, Field2 int not null
, TempGuid uniqueidentifier not null unique
)
insert into #Table1 (Field1, Field2, TempGuid) select 1, 2, newid()
insert into #Table1 (Field1, Field2, TempGuid) select 3, 4, newid()
declare #Table2 table (
Id int not null primary key identity(1, 1)
, Field1 int not null
, Field2 int not null
, TempGuid uniqueidentifier not null unique
)
-- Fill Table2
insert into #Table2 (Field1, Field2, TempGuid)
select Field1, Field2, TempGuid
from #Table1
-- Update Table1 with the identity values from Table2
update a
set a.Id = b.Id
from #Table1 a
join #Table2 b on a.TempGuid = b.TempGuid
-- Show results
select * from #Table1
Output would be workable if you already had a unique key on Table1 that you were inserting into Table2. You could also do a temporary unique key (perhaps GUID again) in a loop or cursor and process one row at a time, but that seems worse to me.
UPDATE
Here is the actual SQL to run on your tables:
-- Add TempGuid columns
alter table Table1 add TempGuid uniqueidentifier null
update Table1 set TempGuid = newid()
alter table Table2 add TempGuid uniqueidentifier not null
-- Fill Table2
insert into Table2 (Field1, Field2, TempGuid)
select Field1, Field2, TempGuid
from Table1
-- Update Table1 with the identity values from Table2
update a
set a.Id = b.Id
from Table1 a
join Table2 b on a.TempGuid = b.TempGuid
-- Remove TempGuid columns
alter table Table1 drop column TempGuid
alter table Table2 drop column TempGuid

Assuming you have full control over the schema definitions, add a foreign key to Table2 that references Table1's primary key.
Perform your data insert:
INSERT INTO Table2 (Field1, Field2, T1PK)
SELECT Field1, Field2, PK FROM Table1
Then backfill Table1:
UPDATE t1 SET Id = t2.PK
FROM Table1 t1 INNER JOIN Table2 t2 ON t2.T1PK = t1.PK
Then delete the extra column (T1PK) from Table2.
Edit:
Since there's no PK in Table1, just add one to Table1, use that, and then drop it at the end.
For example...
ALTER TABLE Table1 ADD COLUMN T1PK UNIQUEIDENTIFIER CONSTRAINT Table1_PK PRIMARY KEY DEFAULT NEWID();
ALTER TABLE Table2 ADD COLUMN T1PK UNIQUEIDENTIFIER NULL
INSERT INTO Table2 (Field1, Field2, T1PK)
SELECT Field1, Field2, T1PK FROM Table1
UPDATE t1 SET Id = t2.PK
FROM Table1 t1 INNER JOIN Table2 t2 ON t2.T1PK = t1.T1PK
ALTER TABLE Table1 DROP CONSTRAINT Table1_PK
ALTER TABLE Table1 DROP COLUMN T1PK
ALTER TABLE Table2 DROP COLUMN T1PK

This is not pretty, but should do as a one time effort.
create table tableA
(
id int,
field1 int,
field2 int
)
create table tableB
(
id int identity(1,1),
field1 int,
field2 int
)
insert into tableA select NULL, 1, 2
insert into tableA select NULL, 2, 3
declare #field1_value int;
declare #field2_value int;
declare #lastInsertedId int;
DECLARE tableA_cursor CURSOR FOR
select field1, field2 from tableA
OPEN tableA_cursor
FETCH NEXT FROM tableA_cursor INTO #field1_value, #field2_value
WHILE ##FETCH_STATUS = 0
BEGIN
insert into tableB select #field1_value, #field2_value
set #lastInsertedId = (SELECT SCOPE_IDENTITY())
update a
set id = #lastInsertedId
from tableA a
where field1 = #field1_value
and field2 = #field2_value
print #field1_value
FETCH NEXT FROM tableA_cursor
INTO #field1_value, #field2_value
END
CLOSE tableA_cursor
DEALLOCATE tableA_cursor
With not exists check:
declare #field1_value int;
declare #field2_value int;
declare #lastInsertedId int;
DECLARE tableA_cursor CURSOR FOR
select field1, field2 from tableA
OPEN tableA_cursor
FETCH NEXT FROM tableA_cursor INTO #field1_value, #field2_value
WHILE ##FETCH_STATUS = 0
BEGIN
IF NOT EXISTS
(
select * from tableB
where field1 = #field1_value
and field2 = #field2_value
)
BEGIN
insert into tableB
select #field1_value, #field2_value
set #lastInsertedId = (SELECT SCOPE_IDENTITY())
update a
set id = #lastInsertedId
from tableA a
where field1 = #field1_value
and field2 = #field2_value
END
FETCH NEXT FROM tableA_cursor
INTO #field1_value, #field2_value
END
CLOSE tableA_cursor
DEALLOCATE tableA_cursor

Related

Insert to table based on another table if doesn’t exist

From doing a bit of research it seems it’s not possible to do an insert with a where clause?
I have a table I want to import into another table where specific record criteria doesn’t already exist. How would I go about this ?
E.g pseudo code -
insert into table b (select * from table a) where not exists tableb.column1 = tablea.column1 and tableb.column2 = tablea.column2
Could this potentially be done with a Join ?
You can insert using INSERT INTO.. SELECT with NOT EXISTS.
Query
insert into [TableB]
select * from [TableA] as [t1] -- [TableA] and [TableB] structure should be same.
where not exists(
select 1 from [TableB] as [t2]
where [t1].[column1] = [t2].[column1]
and [t1].[column2] = [t2].[column2]
);
Or, if the table structure is not same and need to same few columns, then
Query
insert into [TableB]([column1], [column2], ..., [columnN])
select [column1], [column2], ..., [columnN]
from [TableA] as [t1]
where not exists(
select 1 from [TableB] as [t2]
where [t1].[column1] = [t2].[column1]
and [t1].[column2] = [t2].[column2]
);
You can also use LEFT JOIN with IS NULL as below:
INSERT INTO tableb
SELECT a.*
FROM tablea a
LEFT JOIN tableb b ON b.column1 = a.column1
AND b.column2 = a.column2
WHERE b.column1 IS NULL
Referencing table name from INSERT statement in SELECT part will not work, because INSERT is not a query itself, but nothing prevents to query destination table in SELECT, which produces the data set to be inserted.
INSERT INTO tableb (column1, column2)
SELECT column1, column2 FROM tablea
WHERE NOT EXISTS (SELECT * FROM tableb
WHERE tableb.column1 = tablea.column1
AND tabled.column2 = tablea.column2)
Try this :
Via Insert with Select statement with Not Exists
Declare #table1 table(Id int , EmpName varchar(100) )
Declare #table2 table(Id int , EmpName varchar(100) )
Insert into #table1 values (1,'Ajay'), (2, 'Tarak') , (3,'Nirav')
Insert into #table2 values (1,'Ajay')
--Insert into table b (select * from table a) where not exists tableb.column1 = tablea.column1 and tabled.column2 = tablea.column2
INSERT INTO #table2 (id, empname)
select id, empname from #table1
EXCEPT
SELECT id, empname from #table2
Select * from #table1
Select * from #table2
Via merge
Insert into #table2 values (4,'Prakash')
Select * from #table1
Select * from #table2
Declare #Id int , #EmpName varchar(100)
;with data as (select #id as id, #empname as empname from #table1)
merge #table2 t
using data s
on s.id = t.id
and s.empname = t.empname
when not matched by target
then insert (id, empname) values (s.id, s.empname);
Select * from #table1
Select * from #table2

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

SQL : Retrieve inserted row IDs array / table

i have the following statement:
INSERT INTO table1 (field1, FIELD2)
SELECT f1, f2 FROM table2 -- returns 20 rows
after insert i need to know the array/table of IDs generated in table1.ID which is INT IDENTITY
thanx in advance.
Use the OUTPUT clause (SQL2005 and up):
DECLARE #IDs TABLE(ID int)
INSERT INTO table1(Field1, Field2)
OUTPUT inserted.ID into #IDs(ID)
SELECT Field1, Field2 FROM table2
If you want to know exactly which rows from table2 generated which ID in table1 (and Field1 and Field2 aren't enough to identify that), you'll need to use MERGE:
DECLARE #IDs TABLE(Table1ID int, Table2ID int)
MERGE table1 AS T
USING table2 AS S
ON 1=0
WHEN NOT MATCHED THEN
INSERT (Field1, Field2) VALUES(S.Field1, S.Field2)
OUTPUT inserted.ID, S.ID INTO #IDs(Table1ID, Table2ID)
Use SCOPE_IDENTITY()
SELECT SCOPE_IDENTITY()
http://msdn.microsoft.com/en-us/library/ms190315(v=sql.105).aspx

Default values in Insert Select in SQL

How to pass default values in the Insert Select construction in SQL?
I have a table:
create table1 (field1 int, field2 int, field3 int default 1337)
create table2 (field1 int, field2 int)
I want to insert table2 into table1 with a construction similar to this:
insert into table1 select field1, field2, DEFAULT from table2
Is it possible to use something instead of DEFAULT in my example to get the task done? How are previously selected tables usually inserted with default values?
Try
INSERT INTO table1 (field1, field2)
SELECT field1, field2 FROM table2
I tested this using SQL Server 2005
DECLARE #Table1 TABLE(
field1 INT,
field2 INT,
field3 INT DEFAULT 1337
)
INSERT INTO #Table1 (field1,field2,field3) SELECT 1, 2, 3
DECLARE #Table2 TABLE(
field1 INT,
field2 INT
)
INSERT INTO #Table2 (field1,field2) SELECT 15, 16
INSERT INTO #Table1 (field1,field2)
SELECT field1,
field2
FROM #Table2
SELECT * FROM #Table1

Remove duplicate from a table

The database type is PostGres 8.3.
If I wrote:
SELECT field1, field2, field3, count(*)
FROM table1
GROUP BY field1, field2, field3 having count(*) > 1;
I have some rows that have a count over 1. How can I take out the duplicate (I do still want 1 row for each of them instead of +1 row... I do not want to delete them all.)
Example:
1-2-3
1-2-3
1-2-3
2-3-4
4-5-6
Should become :
1-2-3
2-3-4
4-5-6
The only answer I found is there but I am wondering if I could do it without hash column.
Warning
I do not have a PK with an unique number so I can't use the technique of min(...). The PK is the 3 fields.
This is one of many reasons that all tables should have a primary key (not necessarily an ID number or IDENTITY, but a combination of one or more columns that uniquely identifies a row and which has its uniqueness enforced in the database).
Your best bet is something like this:
SELECT field1, field2, field3, count(*)
INTO temp_table1
FROM table1
GROUP BY field1, field2, field3 having count(*) > 1
DELETE T1
FROM table1 T1
INNER JOIN (SELECT field1, field2, field3
FROM table1
GROUP BY field1, field2, field3 having count(*) > 1) SQ ON
SQ.field1 = T1.field1 AND
SQ.field2 = T1.field2 AND
SQ.field3 = T1.field3
INSERT INTO table1 (field1, field2, field3)
SELECT field1, field2, field3
FROM temp_table1
DROP TABLE temp_table1
One possible answer is:
CREATE <temporary table> (<correct structure for table being cleaned>);
BEGIN WORK; -- if needed
INSERT INTO <temporary table> SELECT DISTINCT * FROM <source table>;
DELETE FROM <source table>
INSERT INTO <source table> SELECT * FROM <temporary table>;
COMMIT WORK; -- needed
DROP <temporary table>;
I'm not sure whether the 'work' is needed on transaction statements, nor whether the explicit BEGIN is necessary in PostgreSQL. But the concept applies to any DBMS.
The only thing to beware of is referential constraints and in particular triggered delete operations. If those exist, this may prove less satisfactory.
This will use the OID Object ID (if the table was created with it):
DELETE FROM table1
WHERE OID NOT IN (SELECT MIN (OID)
FROM table1
GROUP BY field1, field2, field3)
Well I should misunderstand something but I'll say :
SELECT DISTINCT field1, field2, field3 FROM table1
Too easy to be good? ^^
This is the simplest method I've found:
Postgre SQL syntax:
CREATE TABLE tmp AS SELECT distinct * FROM table1
truncate table table1
insert into table1 select * from tmp
drop table tmp
T-SQL syntax:
select distinct * into #tmp from table1
truncate table table1
insert into table1 select * from #tmp
drop table #tmp
A good Answer for this problem, but for SQL Server. It uses the ROWCOUNT that SQL Server offers, to good effect. I have never used PostgreSQL and hence don't know the equivalent of ROWCOUNT in PostgreSQL.
Using TSQL, no idea if Postgres supports temp tables but you could select into a temp table, and then loop through and delete and insert your results back into the original
-- **Disclaimer** using TSQL
-- You could select your records into a temp table with a pk
Create Table #dupes
([id] int not null identity(1,1), f1 int, f2 int, f3 int)
Insert Into #dupes (f1,f2,f3) values (1,2,3)
Insert Into #dupes (f1,f2,f3) values (1,2,3)
Insert Into #dupes (f1,f2,f3) values (1,2,3)
Insert Into #dupes (f1,f2,f3) values (2,3,4)
Insert Into #dupes (f1,f2,f3) values (4,5,6)
Insert Into #dupes (f1,f2,f3) values (4,5,6)
Insert Into #dupes (f1,f2,f3) values (4,5,6)
Insert Into #dupes (f1,f2,f3) values (7,8,9)
Select f1,f2,f3 From #dupes
Declare #rowCount int
Declare #counter int
Set #counter = 1
Set #rowCount = (Select Count([id]) from #dupes)
while (#counter < #rowCount + 1)
Begin
Delete From #dupes
Where [Id] <>
(Select [id] From #dupes where [id]=#counter)
and
(
[f1] = (Select [f1] from #dupes where [id]=#counter)
and
[f2] = (Select [f2] from #dupes where [id]=#counter)
and
[f3] = (Select [f3] from #dupes where [id]=#counter)
)
Set #counter = #counter + 1
End
Select f1,f2,f3 From #dupes -- You could take these results and pump them back into --your original table
Drop Table #dupes
Tested this on MS SQL Server 2000. Not familiar with Postgres' options but maybe this will lead you in a right direction.