Avoid duplicates in INSERT INTO SELECT query in SQL Server
Asked Answered
V

11

137

I have the following two tables:

Table1
----------
ID   Name
1    A
2    B
3    C

Table2
----------
ID   Name
1    Z

I need to insert data from Table1 to Table2. I can use the following syntax:

INSERT INTO Table2(Id, Name) SELECT Id, Name FROM Table1

However, in my case, duplicate IDs might exist in Table2 (in my case, it's just "1") and I don't want to copy that again as that would throw an error.

I can write something like this:

IF NOT EXISTS(SELECT 1 FROM Table2 WHERE Id=1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 
ELSE
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1 WHERE Table1.Id<>1

Is there a better way to do this without using IF - ELSE? I want to avoid two INSERT INTO-SELECT statements based on some condition.

Volotta answered 25/3, 2010 at 5:2 Comment(0)
C
268

Using NOT EXISTS:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE NOT EXISTS(SELECT id
                    FROM TABLE_2 t2
                   WHERE t2.id = t1.id)

Using NOT IN:

INSERT INTO TABLE_2
  (id, name)
SELECT t1.id,
       t1.name
  FROM TABLE_1 t1
 WHERE t1.id NOT IN (SELECT id
                       FROM TABLE_2)

Using LEFT JOIN/IS NULL:

INSERT INTO TABLE_2
  (id, name)
   SELECT t1.id,
          t1.name
     FROM TABLE_1 t1
LEFT JOIN TABLE_2 t2 ON t2.id = t1.id
    WHERE t2.id IS NULL

Of the three options, the LEFT JOIN/IS NULL is less efficient. See this link for more details.

Cosmetic answered 25/3, 2010 at 5:7 Comment(9)
Just a clarification on the NOT EXISTS version, you'll need a WITH(HOLDLOCK) hint or no locks will be taken (because there are no rows to lock!) so another thread could insert the row under you.Widera
Interesting, because I have always believed joining to be faster than sub-selects. Perhaps that is for straight joins only, and not applicable to left joins.Lubricant
Duncan, joining is often faster that subselects when they are correlated subqueries. If you have the subquery up in the select list a join will often be faster.Lewison
Thanks! option 2 seems like it would be really inefficient. Unless the database is smart enough to know not to fetch the entire results of the subquery?Whitebook
@Kip: If you read the link I provided that compares the three options, you'd know that your perception is not correct on SQL Server. Could be different on other databases, but the columns compared being nullable or not makes a difference too.Cosmetic
NOT EXISTS is especially useful with composite primary key, NOT IN won't work thenParke
Any ideas why I would I still get cannot insert duplicate key... using any of the above methods?Polaris
@druciferre Possibly a duplicate within the source being inserted.Clubhaul
NOT IN also works in MYSQL ServerEducator
L
51

In MySQL you can do this:

INSERT IGNORE INTO Table2(Id, Name) SELECT Id, Name FROM Table1

Does SQL Server have anything similar?

Lubricant answered 25/3, 2010 at 6:24 Comment(7)
+1 for educating me on this . Very nice syntax. Definitely shorter and better than the one I used. Unfortunately Sql server does not have this.Volotta
Not totally true. When you create a unique index, you can set it to "ignore duplicates", in which case SQL Server will ignore any attempts to add a duplicate.Forelli
And SQL Server still can't... pathetic.Octo
So SQL Server still cant?Ball
And still can'tRef
And still can'tJocosity
And still can'tTrencher
K
6

I just had a similar problem, the DISTINCT keyword works magic:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Kurman answered 15/7, 2016 at 22:10 Comment(1)
Unless I totally misunderstand you, this will work if you have duplicates in the set you're inserting from. It won't, however, help if the set you're inserting from might be duplicates of data already in the insert into table.Coition
P
5

Using ignore Duplicates on the unique index as suggested by IanC here was my solution for a similar issue, creating the index with the Option WITH IGNORE_DUP_KEY

In backward compatible syntax
, WITH IGNORE_DUP_KEY is equivalent to WITH IGNORE_DUP_KEY = ON.

Ref.: index_option

Phonics answered 14/1, 2015 at 16:41 Comment(0)
D
5

I was facing the same problem recently...
Heres what worked for me in MS SQL server 2017...
The primary key should be set on ID in table 2...
The columns and column properties should be the same of course between both tables. This will work the first time you run the below script. The duplicate ID in table 1, will not insert...

If you run it the second time, you will get a

Violation of PRIMARY KEY constraint error

This is the code:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Demo answered 20/10, 2018 at 22:14 Comment(0)
E
4

From SQL Server you can set a Unique key index on the table for (Columns that needs to be unique)

From sql server right click on the table design select Indexes/Keys

Select column(s) that will be not duplicate , then type Unique Key

Elmaelmajian answered 31/7, 2016 at 8:9 Comment(1)
It doesn't response to alternate of INSERT INGORE INTO.Daryldaryle
M
2

A little off topic, but if you want to migrate the data to a new table, and the possible duplicates are in the original table, and the column possibly duplicated is not an id, a GROUP BY will do:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name
Meenen answered 30/1, 2018 at 15:43 Comment(0)
W
0

In my case, I had duplicate IDs in the source table, so none of the proposals worked. I don't care about performance, it's just done once. To solve this I took the records one by one with a cursor to ignore the duplicates.

So here's the code example:

DECLARE @c1 AS VARCHAR(12);
DECLARE @c2 AS VARCHAR(250);
DECLARE @c3 AS VARCHAR(250);


DECLARE MY_cursor CURSOR STATIC FOR
Select
c1,
c2,
c3
from T2
where ....;

OPEN MY_cursor
FETCH NEXT FROM MY_cursor INTO @c1, @c2, @c3

WHILE @@FETCH_STATUS = 0
BEGIN
    if (select count(1) 
        from T1
        where a1 = @c1
        and a2 = @c2
        ) = 0 
            INSERT INTO T1
            values (@c1, @c2, @c3)

    FETCH NEXT FROM MY_cursor INTO @c1, @c2, @c3
END
CLOSE MY_cursor
DEALLOCATE MY_cursor
Webworm answered 14/10, 2020 at 19:59 Comment(0)
I
0

I used a MERGE query to fill a table without duplications. The problem I had was a double key in the tables ( Code , Value ) , and the exists query was very slow The MERGE executed very fast ( more then X100 )

examples for MERGE query

Immediate answered 12/4, 2021 at 13:25 Comment(0)
F
0

For one table it works perfectly when creating one unique index from multiple field. Then simple "INSERT IGNORE" will ignore duplicates if ALL of 7 fields (in this case) will have SAME values.

Select fields in PMA Structure View and click Unique, new combined index will be created.

enter image description here

Fulgor answered 15/6, 2022 at 7:37 Comment(0)
F
-5

A simple DELETE before the INSERT would suffice:

DELETE FROM Table2 WHERE Id = (SELECT Id FROM Table1)
INSERT INTO Table2 (Id, name) SELECT Id, name FROM Table1

Switching Table1 for Table2 depending on which table's Id and name pairing you want to preserve.

Faraday answered 14/11, 2018 at 1:43 Comment(4)
Please don't do this. You're basically saying "whatever data I had is worthless, let's just insert this new data!"Winfordwinfred
@Winfordwinfred If for some reason "Table2" shouldn't getting dropped after the "INSERT" then use the other methods, but this is a perfectly valid way to achieve what the OP asked.Faraday
Valid, but certainly slower and potentially corrupting without a transaction. If you go this route, wrap in a TRANSaction.Saltatory
Despite all the whining about this answer, it may be the best solution depending on the situation.Striper

© 2022 - 2024 — McMap. All rights reserved.