Как обрабатываются одинаковые колонки при JOIN в SQL
Что проверяют
Знаете ли вы, что происходит с одноимёнными колонками на уровне SQL и на уровне Java-кода, который читает результат. Следом часто спрашивают про USING и NATURAL JOIN или про то, что вернёт ResultSet.getLong("id"), если id в выборке два.
Как отвечать
На уровне SQL различайте несколько случаев:
- ссылка на колонку: если
idесть в обеих таблицах,WHERE id = 5илиSELECT idбез префикса база не выполнит. PostgreSQL ответитcolumn reference "id" is ambiguous, MySQL похожей ошибкой. Лечится префиксом таблицы или псевдонима:u.id,o.id; - результат выборки:
SELECT *из соединения спокойно вернёт две колонки с одинаковым именем. СУБД их не переименовывает, суффиксы вродеid_1, если вы их видели, добавил клиент; JOIN ... USING (id): колонка соединения попадает в результат один раз, обращаться к ней можно без префикса. ВFULL JOINеё значение ведёт себя какCOALESCEобеих сторон;NATURAL JOIN: соединяет по всем одноимённым колонкам сразу. Удобно на доске, опасно в проде.
Дальше Java. В JDBC при нескольких колонках с одним именем геттер по имени возвращает первую подходящую, это прямо сказано в документации ResultSet. Исключения не будет, просто в поле orderId тихо окажется id пользователя. То же касается BeanPropertyRowMapper в Spring JdbcTemplate и маппинга нативных запросов в DTO: сопоставление идёт по имени колонки. Поэтому в запросах под маппинг колонки выбирают явно и дают им псевдонимы: u.id AS user_id, o.id AS order_id. Hibernate для JPQL генерирует уникальные псевдонимы сам, но в нативном @Query ответственность снова на вас.
Где спотыкаются
Пишут SELECT * в запросе под маппинг, а потом ищут, откуда в объекте чужой идентификатор. Ошибки нет, данные просто неверные.
Используют NATURAL JOIN. Когда в обе таблицы добавят created_at или status, условие соединения поменяется без единой правки запроса, и строки начнут пропадать.
Уверены, что база сама переименует дубликаты. Это делают отдельные клиенты и библиотеки, и полагаться на такое поведение нельзя.