Тепер настав час поговорити про життя. Про ту його сторону, якої не уникнути: помилки. Ловити, виправляти і розуміти помилки — це обов'язкова частина роботи з даними. Давай розберемо, на які граблі можна наступити при роботі з JOIN в SQL, і як їх обійти.
Помилка 1: Пропуск умови з'єднання — створення декартового добутку
Найчастіша помилка — забути вказати умову з'єднання, використовуючи ON. У такому випадку виникає декартовий добуток, коли кожен рядок з першої таблиці з'єднується з кожним рядком з другої. В результаті ти отримуєш купу рядків, які не мають жодного сенсу і тільки збивають з пантелику.
Ось приклад. Припустимо, у нас є такі таблиці:
Студенти (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
Курси (courses):
| course_id | course_name |
|---|---|
| 101 | Математика |
| 102 | Історія |
Тепер напишемо запит, з'єднавши таблиці через кому без жодної умови (старий ANSI-89 синтаксис, еквівалентний CROSS JOIN):
SELECT *
FROM students, courses;
Результат:
| student_id | name | course_id | course_name |
|---|---|---|---|
| 1 | Otto | 101 | Математика |
| 1 | Otto | 102 | Історія |
| 2 | Anna | 101 | Математика |
| 2 | Anna | 102 | Історія |
Не схоже на правду, так? Цей жах називається декартовим добутком.
Як виправити: використовуй ON, щоб вказати, як пов'язані дані в таблицях.
SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;
І тут з'являється новий розділ помилки...
Захист від дурня
У стандарті SQL (і PostgreSQL це дотримує) JOIN без ON/USING — синтаксична помилка (ERROR: syntax error). Це вимога стандарту SQL-92, а не унікальна фіча PostgreSQL.
Якщо тобі треба декартів добуток — пиши явно CROSS JOIN або старий ANSI-89 синтаксис через кому:
SELECT *
FROM students, courses; -- дасть декартів добуток
Винятки — коли JOIN без ON працює:
- З
NATURAL JOIN— автоматично об'єднує по всіх однойменних колонках (скорочення дляUSING (col1, col2, ...)). Увага: якщо спільних колонок немає — поводиться якCROSS JOIN. При зміні схеми (додаванні колонки в одну з таблиць) запит може несподівано змінити поведінку, тому в проді його зазвичай уникають. - З
USING— вказуєш список колонок, по яких об'єднуються таблиці. CROSS JOIN— завжди без умови, той самий декартів добуток.
Помилка 2: Невірна умова з'єднання
Іноді ти вказуєш умову з'єднання, але робиш це неправильно. Наприклад, з'єднуєш таблиці не по ключах, а по нерелевантних даних.
Припустимо, ми хочемо отримати список студентів і курсів, на які вони записані, але помиляємось і з'єднуємо таблиці по невідповідних полях:
SELECT *
FROM students
JOIN courses
ON students.student_id = courses.course_id;
Такий запит призведе до некоректного результату, бо student_id і course_id — це зовсім різні речі.
Як виправити: переконайся, що використовуєш правильні стовпці для з'єднання. Правильне об'єднання може виглядати так (якщо у тебе є таблиця enrollments, що зв'язує студентів з курсами):
SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
Помилка 3: Дублювання рядків у результаті
Коли ти додаєш кілька JOIN у запит, іноді це призводить до дублювання рядків. Це трапляється, якщо в таблицях JOIN є повторювані записи, або ти неправильно вказав умови об'єднання.
Припустимо, студент Otto двічі записаний на один і той самий курс у таблиці enrollments.
Записи в enrollments:
| student_id | course_id |
|---|---|
| 1 | 101 |
| 1 | 101 |
Тепер запит з JOIN дасть такі результати:
SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
Результат:
| name | course_name |
|---|---|
| Otto | Математика |
| Otto | Математика |
Як виправити: по-перше, переконайся, що у твоїх таблицях немає дублюючих даних. По-друге, якщо це очікувана поведінка, видаляй дублікати за допомогою DISTINCT:
SELECT DISTINCT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
Помилка 4: Втрата рядків при використанні INNER JOIN
INNER JOIN повертає тільки ті рядки, які збігаються в обох таблицях. Якщо в одній з таблиць немає відповідного значення, рядок буде відкинутий. Ти можеш втратити дані, якщо неправильно вибереш тип з'єднання.
Припустимо, у нас є студент, який ще не записаний ні на один курс:
Студенти (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
| 3 | Dhany |
Записи (enrollments):
| student_id | course_id |
|---|---|
| 1 | 101 |
| 2 | 102 |
Тепер запит з INNER JOIN:
SELECT students.name, courses.course_name
FROM students
JOIN enrollments ON students.student_id = enrollments.student_id
JOIN courses ON enrollments.course_id = courses.course_id;
Результат:
| name | course_name |
|---|---|
| Otto | Математика |
| Anna | Історія |
А де Dhany? Якщо ти хочеш включити студентів без курсів, треба використовувати LEFT JOIN:
SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id;
Помилка 5: Неправильна обробка NULL значень
Якщо в одній з таблиць є рядки з порожніми (NULL) значеннями, вони можуть не потрапити у результати (наприклад, при використанні умов фільтрації).
Приклад: ти використовуєш LEFT JOIN, але потім додаєш WHERE для фільтрації.
SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id
WHERE courses.course_name = 'Математика';
Тепер студенти без курсів не потраплять у результат, навіть якщо ти використав LEFT JOIN.
Як виправити: якщо хочеш включити рядки з відсутніми курсами, заміни WHERE на ON або додай додаткову умову:
SELECT students.name, courses.course_name
FROM students
LEFT JOIN enrollments ON students.student_id = enrollments.student_id
LEFT JOIN courses ON enrollments.course_id = courses.course_id
WHERE courses.course_name IS NULL OR courses.course_name = 'Математика';
Помилка 6: Плутанина між типами з'єднань
Ти плутаєшся, який тип з'єднання використовувати. Наприклад, застосовуєш RIGHT JOIN, хоча можна було б скористатися LEFT JOIN, просто помінявши порядок таблиць.
Як уникнути плутанини:
- Використовуй
LEFT JOIN, якщо можеш. Це більш інтуїтивно зрозуміло. - Зміни порядок таблиць, щоб не довелося використовувати
RIGHT JOIN.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ