I have a situation similar to this SO question where I want to copy parent rows and their child rows from and into the same table.
The suggested answer using OUTPUT and MERGE is clean but only considers just one record.
Is there a way to modify it to handle multiple parent rows?
EDIT
I tried to modify the SQL statement and came up with this:
DECLARE #fromId int, #toId int;
SET #fromId = 1;
SET #toId = 2;
DECLARE #mapping TABLE (old_id int, new_id int);
INSERT INTO parent_table (col1, col2, col3)
SELECT #toId, col2, col3
FROM parent_table
WHERE col1 = #fromId;
MERGE child_table tgt
USING (
SELECT t.parent_id, t.col2, t.col3
FROM child_table t
inner join parent_table p on p.id = t.parent_id
WHERE p.col1 = #toId
) src
ON 0 = 1
WHEN NOT MATCHED THEN
INSERT (parent_id, col2, col3) VALUES (src.parent_id, src.col2, src.col3)
OUTPUT src.parent_id, INSERTED.parent_id INTO #mapping (old_id, new_id);
The first INSERT of the parent rows works. However, the second insert does insert any child rows. What am I missing?
You could make use of a UNION operator. We just have to focus on maintaining the column numbers and data types :
select <required-column names, add null if value for a column not required> from parent_table
UNION
select <required-column with null if value for that column not required> from child_table where id in(select id from parent_table);
For e.g. suppose destination has five columns, parent has three columns, child has two columns.
Then, depending on where parent ids should go, we could write :
insert into destination(col1,col2, col3, col4, col5)
select col1, null, col2, col3, null from parent_table where key in(<key values>)
UNION
select col1, col2, null, null,null from child_table where key in(<key values>);
If all records are to be copied :
insert into destination(col1,col2, col3, col4, col5)
select col1, null, col2, col3, null from parent_table
UNION
select col1, col2, null, null,null from child_table where key_column in(select key_column from parent_table);
Related
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);
How can I create a Primary Key in a table. I used the following command to create one, but the key doesn't appear to be enforced:
ALTER TABLE tablename ADD PRIMARY KEY (column1,column3);
Snowflake supports defining and maintaining constraints, but does not enforce them, except for NOT NULL constraints, which are always enforced.
https://docs.snowflake.com/en/sql-reference/constraints-overview.html#supported-constraint-types
Although the primary key is not enforced in Snowflake, you can use the MERGE statement to insert the data to enforce uniqueness of that key. This approach requires making sure that the source data set does not include duplicates, by using for example QUALIFY function.
Example:
CREATE TABLE IF NOT EXISTS test (
col1 VARCHAR(50) NOT NULL,
col2 VARCHAR(50),
col3 VARCHAR(50) NOT NULL,
constraint PK_test primary key (col1, col3) not enforced
);
MERGE INTO TEST AS tgt
USING (
SELECT COL1, COL2, COL3 FROM (
SELECT 'a' COL1, 'b' COL2, 'a' COL3
UNION
SELECT 'a' COL1, 'c' COL2, 'a' COL3
UNION
SELECT 'a' COL1, 'd' COL2, 'a' COL3)
QUALIFY row_number() over (partition by COL1, COL3 order by COL2) = 1
)AS src
ON (tgt.COL1=src.COL1 AND tgt.COL3=src.COL3)
WHEN NOT MATCHED
THEN INSERT (COL1, COL2, COL3)
VALUES (src.COL1, src.COL2, src.COL3);
Only the row a,b,a was inserted.
I am trying to create an Migration Script but i need some help:, i had the very basic script to insert an item into a table but i am trying to do it in a way to check for the item first if it exists, then insert it else skip it
here is my code:
use mydatabase;
INSERT INTO dbo.mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html';
You don't need to repeat the expression just use value construct like that :
INSERT INTO dbo.mytable(col1,col2,col3)
SELECT t.col1, t.col2, t.col3
FROM (values (3, 'test1','test3/abc.html')) t (col1, col2, col3)
WHERE NOT EXISTS (
SELECT 1
FROM dbo.mytable m
WHERE m.col1 = t.col1
AND m.col2 = t.col2
AND m.col3 = t.col3
);
INSERT INTO dbo.mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html'
WHERE NOT EXISTS (
SELECT 1
FROM dbo.mytable
WHERE col1='3'
AND col2='test1'
AND col3='test3/abc.html'
)
You can change the where depending on what you consider already inserted.
Something like this (but you would need some kind of key to identify if the record exists or not) -
IF NOT EXISTS(SELECT 1 FROM dbo.mytable WHERE col1 = '3' AND col2 = 'test1' AND col3 = 'test3/abc.html')
BEGIN
insert into dbo.mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html'
END
Use a MERGE:
CREATE TABLE #mytable (col1 nvarchar(20), col2 nvarchar(20), col3 nvarchar(20))
INSERT INTO #mytable(col1,col2,col3)
SELECT '3','test1','test3/abc.html';
Merge #mytable t
USING (SELECT '3','test1','test3/abc.html') s (col1, col2, col3)
ON t.col1 = s.col1
AND t.col2 = s.col2
AND t.col3 = s.col3
when not matched by target then
INSERT (col1,col2,col3) VALUES (s.col1, s.col2, s.col3);
I am trying to copy multiple records using one query using insert select from.
Insert into tab_A(colId, col1, col2, col3)
Select colId, col1, col2, col3 form tab_A
Where colId in ( 2,4,6)
Would it be possible to assign different colId for new entries? For example colid 2 should be replaced with 23, 4 with 24 and 6 with 25. How could I achieve it in a single query?
this would work
Insert into tab_A(colId, col1, col2, col3)
Select 23 , col1, col2, col3 form tab_A Where colId = 2 UNION ALL
Select 24 , col1, col2, col3 form tab_A Where colId = 4 UNION ALL
Select 25 , col1, col2, col3 form tab_A Where colId = 6
If you give some more info I could provide somthing more reusable. Should/is colId (be) an identity column?
EDIT
This would work in this very specialised case
Insert into tab_A(colId, col1, col2, col3)
Select ((colId - 4) * (-1)) + colId + 20 , col1, col2, col3
form tab_A Where colId IN (2, 4, 6)
The function newId = ((oldId - 4) * (-1)) + oldId + 20 is obviously specific to the stated problem.
EDIT2
I suspect somthing like this is more generic approach is appropriate.
DECLARE #MaxColID INT
BEGIN TRANSACTION
SELECT #MaxColID = MAX(ColID) FROM tab_A
INSERT tab_A(colId, col1, col2, col3)
SELECT row + #MaxColID, col1, col2, col3
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY ColID) row, col1, col2, col3
FROM tab_A WHERE colID IN (2, 4, 6)
)
COMMIT
EDIT 3
If you think EDIT 2 is actually what you want then you really want to make ColID an IDENTITY column, then you could do this.
INSERT tab_A (col1, col2, col3)
SELECT col1, col2, col3 FROM tab_A WHERE colId IN (2, 4, 6)
I dont see col4 or col6 in your query, but is this what you want:
Insert into tab_A(colId, col1, col2, col3)
Select colId, col1, 23, col3 form tab_A
Where colId in ( 2,4,6)
have you just tried adding the disired difference to colId -
In your case, since you need to replace 2 by 23, difference is 21.
Insert into tab_A(colId, col1, col2, col3)
Select colId+21, col1, col2, col3
form tab_A Where colId in ( 2,4,6)
Note: I missed the part, that the differnce is not consistent in your case.
The proposed solution will work only if difference is same
There are a few options:
Add the new ID column to the original table and populate it with the new values before you do this insert, selecting the new ID column instead of the old. This would be the tidiest solution I think.
Alternative - Modify the ID value on the insert based on a rule e.g.
INSERT INTO tab_A(colID, col1, col2, col3)
SELECT colId + 20, col1, col2, col3
FROM tab_A
WHERE colID IN(2,4,6)
Last resort - Process the insert sequentially with a cursor, modifying the ID value each time.
You could also write case in the select. when 2 then 23 or whatever value.
I'm doing an
INSERT INTO table1...
SELECT...
FROM table2
However, I need to retrieve the identity from a table3 and do an insert into it just before inserting into table1. These two inserts need to occur together, with table3 insert going first. I've tried something like this:
INSERT INTO table1 (col1, col2, col3)
SELECT (
col1=(insert into table3(col1, col2)values(1,1) select SCOPE_IDENTITY(),
col2, col3)
FROM table2
But that doesn't work. table1.col1 does need the identity value from the new insert into table3. Amount of data to insert probably no more than a few 100 rows. Any suggestions?
It looks like you might be able to use the Output Clause.
BEGIN TRANSACTION
DECLARE #MyResults table(col1 int, col2 int, col3 int);
INSERT INTO table3 (col1, col2)
OUTPUT SCOPE_IDENTITY(), table2.col2, table2.col3 INTO #MyResults
SELECT 1, 1 FROM table2
INSERT INTO table1 (col1, col2, col3)
SELECT col1, col2, col3 FROM #MyResults
COMMIT