В таблицу загружают данные через запрос источник, и в результате появляются одинаковые строки. Как SQL опре...

В таблицу загружают данные через запрос-источник, и в результате появляются одинаковые строки. Как SQL определяет, сколько строк будет вставлено?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

INSERT ... SELECT вставляет одну строку для каждой строки результата запроса-источника. SQL не удаляет дубликаты автоматически: если источник вернул две одинаковые строки, обе будут переданы на вставку.

Дубликаты исчезнут только при явном устранении повторов, например через DISTINCT, группировку или другую логику дедупликации. Ограничение UNIQUE не удаляет дубликаты, а обычно отклоняет вставку, если повтор нарушает ограничение.

Исторический контекст

SQL по умолчанию работает с семантикой мультимножеств: одинаковые строки могут присутствовать в результате несколько раз. Такой подход сохраняет кратность данных, что важно для корректного поведения агрегатных функций вроде COUNT и не требует неявного сравнения всех строк для удаления повторов.

Автоматическая дедупликация сделала бы обычные запросы дороже и изменила бы смысл операций, где количество повторений имеет значение. Поэтому устранение дубликатов является явным решением разработчика, а не скрытым свойством SELECT или INSERT.

Постановка проблемы

Источник для загрузки может сформировать одинаковые строки из-за соединения таблиц, повторных событий, отсутствия уникального ключа или ошибочной логики отбора. При использовании INSERT ... SELECT каждая такая строка становится отдельной попыткой вставки.

Это может привести к переполнению целевой таблицы, ошибке нарушения уникальности или появлению логически дублирующихся записей. Особенно опасно, когда разработчик предполагает, что одинаковые значения автоматически будут считаться одной сущностью.

Подробное решение

Логика работы состоит из двух шагов: сначала выполняется запрос-источник, затем каждая строка его результата вставляется в целевую таблицу. Сам факт совпадения значений не влияет на количество строк результата.

CREATE TABLE target ( id INTEGER, status VARCHAR(10) ); WITH source(id, status) AS ( VALUES (1, 'new'), (1, 'new') ) INSERT INTO target (id, status) SELECT id, status FROM source;

В этом примере в target формируются две строки, даже несмотря на полное совпадение значений. Если требуется оставить только уникальные комбинации значений, дедупликацию нужно выразить явно через SELECT DISTINCT или через группировку.

UNIQUE решает другую задачу: оно защищает таблицу от повторения ключа, но не выбирает автоматически, какую из строк сохранить. В зависимости от СУБД и режима выполнения нарушение ограничения может привести к отклонению оператора, использованию специальной стратегии конфликта или откату транзакции.

Важно различать физическое совпадение строк и бизнес-дубликат. Две строки могут быть одинаковыми по отображаемым полям, но различаться идентификатором, временем события или техническими атрибутами; поэтому без определения бизнес-ключа безопасная дедупликация невозможна.

Ситуация из практики

Команда загружала события заказов в таблицу фактов через INSERT ... SELECT. После добавления соединения с таблицей тегов одно событие стало соответствовать нескольким строкам, поэтому каждый заказ начал вставляться повторно.

Рассматривались два варианта. Добавление DISTINCT было простым, но могло скрыть реальные различия в неключевых полях и не объясняло, какую запись считать актуальной. Уникальное ограничение защищало таблицу, но приводило к ошибкам загрузки и не решало вопрос выбора победившей строки.

Выбрали явную дедупликацию по бизнес-ключу события с правилом выбора самой новой записи. Это сохранило предсказуемость загрузки и позволило контролировать конфликтующие данные; после исправления соединения число вставляемых строк стало соответствовать числу уникальных событий.

Что кандидаты часто упускают

1. Удаляет ли GROUP BY все дубликаты результата?

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

Например, группировка может посчитать количество повторений, а не просто оставить произвольную строку. Поэтому выбор между DISTINCT и GROUP BY должен зависеть от цели: убрать повторы или вычислить показатели по группам.

2. Чем отличается UNION от UNION ALL при последующей вставке?

UNION обычно устраняет дубликаты между объединяемыми результатами, а UNION ALL сохраняет все строки, включая повторяющиеся. Если результат такого объединения используется в INSERT ... SELECT, разница напрямую влияет на число вставленных строк.

Однако устранение повторов при UNION относится к результату объединения, а не является общим свойством INSERT. Другие источники дубликатов, например соединения или повторяющиеся строки внутри одного источника, нужно анализировать отдельно.

3. Что произойдёт, если в целевой таблице есть уникальное ограничение?

Каждая строка результата всё равно будет обработана как отдельная вставка. Если две строки имеют одинаковое значение уникального ключа, операция столкнётся с нарушением ограничения; точные последствия зависят от СУБД, настроек конфликта и границ транзакции.

Уникальное ограничение полезно как последний уровень защиты целостности, но не заменяет корректную подготовку источника. Если нужно выбрать одну строку из нескольких кандидатов, это правило следует выразить в запросе явно, например через ранжирование по времени или приоритету.