Тепер настав час поговорити про життя. Про ту його сторону, якої не уникнути: помилки. Ловити, виправляти і розуміти помилки — це обов'язкова частина роботи з даними. Давай розберемо, на які граблі можна наступити при роботі з JOIN в SQL, і як їх обійти.
Помилка 1: Пропуск умови з'єднання — створення декартового добутку
Найчастіша помилка — забути вказати умову з'єднання, використовуючи ON. У такому випадку виникає декартовий добуток, коли кожен рядок з першої таблиці з'єднується з кожним рядком з другої. В результаті ти отримуєш купу рядків, які не мають жодного сенсу і тільки збивають з пантелику.
Ось приклад. Припустимо, у нас є такі таблиці:
Студенти (students):
| student_id | name |
|---|---|
| 1 | Otto |
| 2 | Anna |
Курси (courses):
| course_id | course_name |
|---|---|
| 101 | Математика |
| 102 | Історія |
Тепер напишемо запит, забувши про ON:
SELECT *
FROM students
JOIN 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;
І тут з'являється новий розділ помилки...
Захист від дурня
Це настільки поширена проблема, що в PostgreSQL заборонили використання JOIN без ON та умови.
Якщо тобі треба об'єднати кожен рядок з кожним, можна використати синтаксис без JOIN:
SELECT *
FROM students, courses;
Ще варіант 3 варіант - коли JOIN без ON працює:
- З
NATURAL 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.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ