Рассмотрите запрос с соединением по USING. Какой набор столбцов вернёт результат и почему ключ id представлен только один раз?
WITH left_src(id, left_value) AS (
VALUES (1, 'A'), (2, 'B')
), right_src(id, right_value) AS (
VALUES (2, 'B2'), (3, 'C')
)
SELECT *
FROM left_src
FULL OUTER JOIN right_src USING (id);
USING (id) формирует в результирующей таблице один объединённый столбец id, а не две копии left_src.id и right_src.id. Для FULL OUTER JOIN его значение берётся из доступной стороны: логически это COALESCE(left_src.id, right_src.id).
Результат содержит столбцы id, left_value, right_value и три строки: для ключей 1, 2 и 3. У ключа 1 правые поля будут NULL, у ключа 3 — левые.
Синтаксис USING появился как компактная форма записи соединений по одноимённым столбцам. Он решает практическую проблему дублирования ключевых столбцов в результирующем наборе и явно сообщает, что столбцы с указанными именами являются общими ключами соединения.
Альтернативой является ON, где условие задаётся полностью, например l.id = r.id. Такой вариант гибче, но при выборе l.* и r.* сохраняет обе копии ключа.
При FULL OUTER JOIN часть строк существует только слева, а часть — только справа. Если результат должен содержать единый идентификатор, выбор только l.id потеряет идентификаторы правой стороны, а выбор только r.id — левой.
USING меняет не только краткость записи условия, но и схему результата. Это важно для SELECT *, представлений, клиентского кода и запросов, которые обращаются к столбцам по позиции или имени.
В запросе совпадающие значения ключа 2 соединяются в одну строку. Для ключа 1 правая строка отсутствует, поэтому результат содержит id = 1, left_value = 'A' и right_value = NULL. Для ключа 3 ситуация зеркальная.
USING допустим, когда соединяемые столбцы имеют одинаковые имена. Если имена различаются или условие сложнее равенства, например содержит диапазон или дополнительные предикаты, нужно использовать ON.
Для внешних соединений объединённый столбец USING представляет общий ключ результата. В случае FULL OUTER JOIN он сохраняет непустой ключ любой из сторон. При INNER JOIN совпавшие значения равны по условию соединения, поэтому практическая разница обычно проявляется именно в форме результата, а не в значениях.
Обычные неключевые столбцы с одинаковыми именами USING не объединяет: если соединение выполнено только по id, одноимённые left_value и right_value всё равно остаются отдельными столбцами. Поэтому USING не является аналогом NATURAL JOIN: список ключей в нём задан явно, а не выводится из всей схемы.
Явное перечисление столбцов обычно надёжнее SELECT *, особенно в представлениях и коде приложений. Оно защищает от неожиданного изменения схемы результата при добавлении новых столбцов в исходные таблицы.
Сервис сверяет справочники клиентов из двух систем. Разработчик использовал FULL OUTER JOIN ... ON l.client_id = r.client_id, после чего выбрал l.client_id и потерял идентификаторы клиентов, существующих только в правой системе.
Вариант с COALESCE(l.client_id, r.client_id) явно исправляет значение и работает при разных именах столбцов. Его минус — необходимость вручную поддерживать выражение и отдельные имена ключей.
Вариант FULL OUTER JOIN ... USING (client_id) короче и сразу формирует единый ключ. Он был выбран после приведения имён ключей к общему соглашению; результатом стали корректные строки для клиентов, присутствующих только в одной системе, без дублирования client_id.
Вопрос: Можно ли использовать USING, если ключи называются customer_id и client_id?
Ответ: Нет, USING требует общего имени столбца в обеих таблицах. Нужно переименовать столбец в подзапросе или использовать ON, например ON l.customer_id = r.client_id. При разных именах ON также позволяет явно контролировать, какой ключ выводить или объединять через COALESCE.
Вопрос: Объединит ли USING (id) все одноимённые столбцы id, name и status?
Ответ: Нет, объединяется только столбец, перечисленный в USING, то есть id. Остальные одноимённые столбцы не становятся общими автоматически и обычно требуют квалификации, например left_src.name и right_src.name. Это принципиальное отличие USING от NATURAL JOIN, который автоматически использует все совпадающие имена и поэтому чувствителен к изменениям схемы.
Вопрос: Что произойдёт, если в одной из таблиц несколько строк имеют один и тот же id?
Ответ: USING не устраняет дубликаты. Соединение создаст комбинацию каждой подходящей строки слева с каждой подходящей строкой справа. Например, две строки слева и три строки справа с одним ключом дадут шесть строк для этого ключа. Для получения одной строки нужно отдельно определить правило дедупликации, например применить агрегацию или ROW_NUMBER() до соединения.