Онлайн час в Google Meet
Лого на 91. НЕГ „Проф. Константин Гълъбов“

12. клас · Избираем модул · Урок 3

Съединения на таблици (JOIN) — видове и типични грешки

INNER, LEFT, RIGHT и FULL JOIN върху едни и същи данни: кой ред изчезва, къде се появява NULL, защо редовете се дублират при 1:M и как COUNT(*) и COUNT(колона) броят различно.

Едни и същи данни, три различни отговора

В Модул 3 научихте, че JOIN събира обратно разкъсаното при нормализацията. Днес гледаме отблизо какво точно се случва с редовете. В учебната база има 9 ученици и 12 оценки, но трима ученици — Емил, Захари и Ива — нямат нито една оценка. Тази дребна подробност решава дали справката „ученици и оценки“ ще има 12, 15 или 108 реда. Три пъти ще пуснете почти една и съща заявка и три пъти ще получите различен резултат.

Съединение (JOIN)

Операция, която образува нова таблица от двойки редове на две таблици, за които е изпълнено условието след ON. Видът на съединението определя какво става с редовете без двойка.

ABINNER JOINABLEFT JOINABRIGHT JOINABFULL JOIN
A е лявата таблица (преди JOIN), B — дясната (след JOIN). Оцветено е това, което влиза в резултата.

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 не е NULL0
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 вместо 0COUNT(колона от дясната таблица)
Филтър по дясната таблица в WHERELEFT 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 — декартово произведение: редовете се умножават.