Избегайте дублирования в запросе INSERT INTO SELECT в SQL Server

109

У меня есть две следующие таблицы:

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

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

Мне нужно вставить данные из Table1в Table2. Я могу использовать следующий синтаксис:

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

Однако в моем случае могут существовать повторяющиеся идентификаторы Table2(в моем случае это просто " 1"), и я не хочу копировать это снова, так как это вызовет ошибку.

Я могу написать примерно так:

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

Есть ли лучший способ сделать это без использования IF - ELSE? Я хочу избежать двух INSERT INTO-SELECTутверждений, основанных на каком-то условии.

Ашиш Гупта
источник

Ответы:

201

Использование 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)

Использование 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)

Использование 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

Из трех вариантов LEFT JOIN/IS NULLменее эффективен. См. Эту ссылку для получения более подробной информации .

OMG Пони
источник
9
Просто пояснение к версии NOT EXISTS, вам понадобится подсказка WITH (HOLDLOCK), иначе блокировки не будут приняты (потому что нет строк для блокировки!), Чтобы другой поток мог вставить строку под вами.
IDisposable
3
Интересно, потому что я всегда считал, что присоединение происходит быстрее, чем подвыборка. Возможно, это только для прямых соединений и не применимо к левым соединениям.
Дункан
1
Дункан, присоединение часто происходит быстрее, чем подзапросы, если они являются коррелированными подзапросами. Если у вас есть подзапрос в списке выбора, соединение часто будет быстрее.
HLGEM
9
NOT EXISTSособенно полезен с составным первичным ключом, NOT INтогда не будет работать
Tomash
1
@OMGPonies - ваша ссылка для получения дополнительной информации кажется мертвой. У вас есть другой, который может быть полезен?
FreeMan
36

В MySQL вы можете сделать это:

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

Есть ли у SQL Server что-нибудь подобное?

Дункан
источник
5
+1 за то, что научил меня этому. Очень красивый синтаксис. Определенно короче и лучше того, что я использовал. К сожалению, на сервере Sql этого нет.
Ашиш Гупта,
13
Не совсем так. Когда вы создаете уникальный индекс, вы можете установить для него «игнорировать дубликаты», и в этом случае SQL Server будет игнорировать любые попытки добавить дубликат.
IamIC
2
А SQL Server по-прежнему не может ... жалко.
Smack Jack
1
Итак, SQL Server все еще не работает?
Ингус
8

У меня была аналогичная проблема, ключевое слово DISTINCT работает волшебно:

INSERT INTO Table2(Id, Name) SELECT DISTINCT Id, Name FROM Table1
Хантер Бингхэм
источник
21
Разве я совершенно неправильно понять вас, это будет работать , если у вас есть дубликаты в наборе вы вставляете с . Однако это не поможет, если набор, который вы вставляете, может быть дубликатом данных, уже находящихся в insert intoтаблице.
FreeMan
5

Недавно я столкнулся с той же проблемой ...
Вот что сработало для меня в MS SQL server 2017 ...
Первичный ключ должен быть установлен на ID в таблице 2 ...
Столбцы и свойства столбцов должны быть одинаковыми, конечно, между обоими таблицы. Это сработает при первом запуске приведенного ниже сценария. Повторяющийся идентификатор в таблице 1 не будет вставлен ...

Если вы запустите его второй раз, вы получите

Нарушение ограничения PRIMARY KEY ошибка

Это код:

Insert into Table_2
Select distinct *
from Table_1
where table_1.ID >1
Вишан Найкер
источник
4

Использование ignore Duplicatesуникального индекса, как было предложено IanC, было моим решением аналогичной проблемы, создание индекса с помощью OptionWITH IGNORE_DUP_KEY

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

Ссылка: index_option

Tazz602
источник
4

Из SQL Server вы можете установить индекс уникального ключа в таблице для (столбцы, которые должны быть уникальными)

На сервере sql щелкните правой кнопкой мыши дизайн таблицы и выберите «Индексы / ключи».

Выберите столбцы, которые не будут дублироваться, затем введите уникальный ключ

М. Салах
источник
1

Немного не по теме, но если вы хотите перенести данные в новую таблицу, а возможные дубликаты находятся в исходной таблице , а возможно дублированный столбец не является идентификатором, GROUP BYподойдет:

INSERT INTO TABLE_2
(name)
  SELECT t1.name
  FROM TABLE_1 t1
  GROUP BY t1.name
FullStackFool
источник
-1

Достаточно простого DELETEперед тем INSERT:

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

Переключение Table1в Table2зависимости от того , таблицы Idи nameсопряжения вы хотите сохранить.

Сакро
источник
3
Пожалуйста, не делай этого. Вы в основном говорите: «Все данные, которые у меня были, ничего не стоят, давайте просто вставим эти новые данные!»
Andir
@Andir Если по какой-то причине «Table2» не должен отбрасываться после «INSERT», тогда используйте другие методы, но это вполне допустимый способ добиться того, о чем просил OP.
Sacro
1
Действителен, но определенно медленнее и может привести к повреждению без транзакции. Если вы пойдете по этому маршруту, завершите транзакцию.
MC9000,