Едни и същи данни, три различни отговора
В Модул 3 научихте, че JOIN събира обратно разкъсаното при нормализацията. Днес гледаме отблизо какво точно се случва с редовете. В учебната база има 9 ученици и 12 оценки, но трима ученици — Емил, Захари и Ива — нямат нито една оценка. Тази дребна подробност решава дали справката „ученици и оценки“ ще има 12, 15 или 108 реда. Три пъти ще пуснете почти една и съща заявка и три пъти ще получите различен резултат.
Съединение (JOIN)
Операция, която образува нова таблица от двойки редове на две таблици, за които е изпълнено условието след ON. Видът на съединението определя какво става с редовете без двойка.
INNER JOIN — само двойките
SELECT s.name, m.mark
FROM students s
INNER JOIN marks m ON m.student_id = s.id;- Резултатът има точно толкова реда, колкото са двойките — тук 12, колкото са оценките.
- Ученик без оценка няма двойка → изчезва от резултата. Никой не ви казва, че го няма.
- Ученик с две оценки се появява на два реда. Това е дублиране при 1:M: „един“ ред отляво се повтаря за всеки „много“ ред отдясно. Не е грешка — но ако след това броите или сумирате колони на ученика, ще ги преброите два пъти.
INNERможе да се пропусне:JOINозначаваINNER JOIN.
LEFT JOIN — всичко отляво, NULL отдясно
SELECT s.name, m.mark
FROM students s
LEFT JOIN marks m ON m.student_id = s.id;Всички редове от лявата таблица влизат в резултата. Който има двойка — както при INNER; който няма — с NULL във всички колони от дясната таблица. Резултатът има 12 + 3 = 15 реда.
NULL-овете са полезни: „кой няма“
Редовете с NULL отдясно са точно тези без съответствие. Филтър върху тях дава „учениците без оценки“ — същото, което миналия час направихте с NOT EXISTS.
SELECT s.name
FROM students s
LEFT JOIN marks m ON m.student_id = s.id
WHERE m.id IS NULL;Как се броят NULL-овете
| Израз | Брои | За ученик без оценки дава |
|---|---|---|
COUNT(*) | всички редове на групата | 1 (редът с NULL е ред) |
COUNT(m.id) | редовете, в които m.id не е NULL | 0 |
COUNT(DISTINCT m.subject_id) | различните не-NULL стойности | 0 |
RIGHT JOIN и FULL JOIN
RIGHT JOIN е огледалният LEFT JOIN: запазва всички редове от дясната таблица. Всеки RIGHT JOIN може да се напише като LEFT JOIN с разменени таблици — и повечето хора правят точно това, защото „таблицата, която трябва да остане цяла, е отляво“ се чете по-лесно. Ще го видите в третия пример: искаме всички предмети, дори тези без оценки.
-- Едно и също:
SELECT sub.name, m.mark FROM marks m RIGHT JOIN subjects sub ON sub.id = m.subject_id;
SELECT sub.name, m.mark FROM subjects sub LEFT JOIN marks m ON m.subject_id = sub.id;FULL JOIN (FULL OUTER JOIN) запазва редовете без двойка и от двете страни — учениците без оценки и оценките без ученик. Полезен е при сверяване на два списъка: кое има само в единия, кое само в другия.
Типични грешки
| Грешка | Симптом | Поправка |
|---|---|---|
| Забравено условие ON (или JOIN през запетая без WHERE) | декартово произведение: 9 × 12 = 108 реда, всеки с всеки | винаги ON; при много таблици — по едно условие на JOIN |
| INNER там, където трябва LEFT | редове „изчезват“ без грешка | проверете броя редове спрямо очаквания; LEFT JOIN + IS NULL показва кои липсват |
| COUNT(*) след LEFT JOIN | групи без съответствие излизат с 1 вместо 0 | COUNT(колона от дясната таблица) |
| Филтър по дясната таблица в WHERE | LEFT JOIN се държи като INNER | условието в ON |
| Еднакви имена на колони (name в students и в classes) | „ambiguous column name“ или грешна колона | псевдоними: s.name, c.name AS klas |
| JOIN по грешна колона (напр. m.id = s.id) | изпълнява се, но двойките са безсмислени | съединявайте външен ключ с първичен ключ |
Какво трябва да запомниш
- INNER JOIN — само двойките; редовете без съответствие изчезват мълчаливо.
- LEFT JOIN — всичко отляво, NULL отдясно; RIGHT — огледално; FULL — и двете (няма го в MySQL).
- При 1:M редът „едно“ се повтаря за всеки ред „много“ — бройте с DISTINCT или групирайте.
- COUNT(*) брои и празните редове; COUNT(m.id) — само истинските съответствия.
- Филтър по дясната таблица — в ON, не в WHERE, иначе LEFT става INNER.
- Без ON — декартово произведение: редовете се умножават.