Multi Table Databases

OUTER JOINs - LEFT, RIGHT, FULL

Детальний розбір LEFT JOIN, RIGHT JOIN, FULL OUTER JOIN - синтаксис, NULL обробка, практичні сценарії

OUTER JOINs - LEFT, RIGHT, FULL

Проблема: INNER JOIN втрачає дані

Згадаймо приклад з попереднього файлу:

-- Дані
Departments: Software Dev, Mathematics, Physics
Teachers: Sophia (Software Dev), Henry (Software Dev), Emma (Mathematics)

-- INNER JOIN
SELECT T.FirstName, D.Name
FROM Teachers AS T
INNER JOIN Departments AS D
    ON T.DepartmentId = D.Id;

-- Результат: 3 рядки
-- ❌ Physics НЕ відображається (немає викладачів)

Питання: Як показати всі кафедри, навіть якщо на них немає викладачів?

Відповідь: OUTER JOIN!


1. Що таке OUTER JOIN?

OUTER JOIN — з'єднання таблиць, що повертає:

  • Всі збіги (як INNER JOIN)
  • + рядки без збігів з однієї або обох таблиць
Loading diagram...
graph TD
    A[OUTER JOIN] --> B[LEFT JOIN]
    A --> C[RIGHT JOIN]
    A --> D[FULL OUTER JOIN]

    B --> E[Всі рядки з лівої + збіги]
    C --> F[Всі рядки з правої + збіги]
    D --> G[Всі рядки з обох таблиць]

    style A fill:#3b82f6
    style B fill:#10b981
    style C fill:#f59e0b
    style D fill:#ef4444

2. LEFT JOIN (LEFT OUTER JOIN)

2.1. Концепція

LEFT JOIN повертає:

  • Всі рядки з лівої таблиці (FROM)
  • ✅ Збіги з правої таблиці
  • ⚠️ NULL для правої таблиці, якщо немає збігу
Loading diagram...
%%{init: {'theme':'base'}}%%
flowchart LR
    A[Ліва таблиця<br/>100%] -->|LEFT JOIN| C[Результат]
    B[Права таблиця<br/>тільки збіги] -.->|збіги| C

    style A fill:#10b981,color:#fff
    style B fill:#94a3b8,color:#000
    style C fill:#3b82f6,color:#fff

2.2. Синтаксис

SELECT columns
FROM table1
LEFT JOIN table2
    ON table1.column = table2.column;

-- Або повна назва
LEFT OUTER JOIN

2.3. Приклад

SELECT
    D.Name AS Department,
    T.FirstName + ' ' + T.LastName AS Teacher
FROM Departments AS D  -- Ліва таблиця (всі рядки)
LEFT JOIN Teachers AS T  -- Права таблиця (тільки збіги)
    ON D.Id = T.DepartmentId
ORDER BY D.Name;

Результат:

DepartmentTeacher
MathematicsEmma Kirk
PhysicsNULL
Software DevelopmentSophia Nelson
Software DevelopmentHenry MacAlister
Physics з'явилась з Teacher = NULL, бо немає викладачів на цій кафедрі!

2.4. Візуалізація LEFT JOIN

Departments (Ліва - ВСІ)     Teachers (Права - збіги)
┌────┬──────────────┐         ┌────┬─────────┬────────┐
│ Id │ Name         │         │ Id │ Name    │ DeptId │
├────┼──────────────┤         ├────┼─────────┼────────┤
│ 1  │ Software Dev │ ────┬───│ 1  │ Sophia  │   1    │
│ 2  │ Mathematics  │ ────┼───│ 2  │ Henry   │   1    │
│ 3  │ Physics      │ ✗   └───│ 3  │ Emma    │   2    │
└────┴──────────────┘         └────┴─────────┴────────┘

        ↓ LEFT JOIN ↓

Результат (ВСІ кафедри + збіги)
┌──────────────┬─────────┐
│ Department   │ Teacher │
├──────────────┼─────────┤
│ Software Dev │ Sophia  │
│ Software Dev │ Henry   │
│ Mathematics  │ Emma    │
│ Physics      │ NULL    │ ← Немає викладачів
└──────────────┴─────────┘

2.5. Діаграма Венна LEFT JOIN

Loading diagram...
graph TD
    subgraph "LEFT JOIN"
        direction LR
        L[Ліва таблиця<br/>ПОВНІСТЮ]
        R[Права таблиця<br/>тільки перетин]
        L -.->|+ NULL| Result[Результат]
        R -->|збіги| Result
    end

    style L fill:#10b981,color:#fff
    style R fill:#cbd5e1,color:#000
    style Result fill:#3b82f6,color:#fff

3. RIGHT JOIN (RIGHT OUTER JOIN)

3.1. Концепція

RIGHT JOINдзеркало LEFT JOIN:

  • Всі рядки з правої таблиці (після JOIN)
  • ✅ Збіги з лівої таблиці
  • ⚠️ NULL для лівої таблиці, якщо немає збігу
У практиці: RIGHT JOIN використовується рідко - завжди можна переписати як LEFT JOIN (поміняти таблиці місцями).

3.2. Синтаксис

SELECT columns
FROM table1
RIGHT JOIN table2
    ON table1.column = table2.column;

-- Або повна назва
RIGHT OUTER JOIN

3.3. Приклад

-- Студенти БЕЗ групи (якщо такі є)
INSERT INTO Students (FirstName, LastName, GroupId) VALUES
('John', 'Doe', 1),
('Jane', 'Smith', 2),
('Orphan', 'Student', NULL);  -- Студент БЕЗ групи!

-- RIGHT JOIN - показати ВСІ студентів
SELECT
    G.Name AS GroupName,
    S.FirstName + ' ' + S.LastName AS Student
FROM Groups AS G          -- Ліва (може бути NULL)
RIGHT JOIN Students AS S  -- Права (ВСІ рядки)
    ON G.Id = S.GroupId
ORDER BY G.Name, S.LastName;

Результат:

GroupNameStudent
NULLOrphan Student
31PR11John Doe
32PR31Jane Smith

3.4. LEFT vs RIGHT: Еквівалентність

Ці два запити ідентичні:

-- Показати ВСІ студентів + їх групи
SELECT G.Name, S.FirstName
FROM Students AS S       -- Ліва (ВСІ)
LEFT JOIN Groups AS G    -- Права (збіги)
    ON S.GroupId = G.Id;
Best Practice: Завжди використовуйте LEFT JOIN замість RIGHT JOIN (просто поміняйте таблиці місцями для кращої читабельності).

4. FULL OUTER JOIN (FULL JOIN)

4.1. Концепція

FULL OUTER JOIN повертає:

  • Всі рядки з обох таблиць
  • ✅ Збіги
  • ⚠️ NULL для лівої таблиці, якщо немає збігу справа
  • ⚠️ NULL для правої таблиці, якщо немає збігу зліва
Loading diagram...
graph LR
    A[Ліва таблиця<br/>ВСІ рядки] -->|+ NULL| C[Результат]
    B[Права таблиця<br/>ВСІ рядки] -->|+ NULL| C

    style A fill:#10b981,color:#fff
    style B fill:#f59e0b,color:#000
    style C fill:#ef4444,color:#fff

4.2. Синтаксис

SELECT columns
FROM table1
FULL OUTER JOIN table2
    ON table1.column = table2.column;

-- Або коротко
FULL JOIN

4.3. Приклад

-- Дані
Departments: Software Dev (Id=1), Mathematics (Id=2), Physics (Id=3)
Teachers: Sophia (DeptId=1), Henry (DeptId=1), Emma (DeptId=2), John (DeptId=NULL)

-- FULL OUTER JOIN
SELECT
    D.Name AS Department,
    T.FirstName + ' ' + T.LastName AS Teacher
FROM Departments AS D
FULL OUTER JOIN Teachers AS T
    ON D.Id = T.DepartmentId
ORDER BY D.Name, T.FirstName;

Результат:

DepartmentTeacher
NULLJohn Doe
MathematicsEmma Kirk
PhysicsNULL
Software DevelopmentHenry MacAlister
Software DevelopmentSophia Nelson

Що бачимо:

  • ✅ Physics (кафедра БЕЗ викладачів) - Teacher = NULL
  • ✅ John Doe (викладач БЕЗ кафедри) - Department = NULL

4.4. Візуалізація FULL OUTER JOIN

Departments               Teachers
┌────┬──────────────┐    ┌────┬──────┬────────┐
│ Id │ Name         │    │ Id │ Name │ DeptId │
├────┼──────────────┤    ├────┼──────┼────────┤
│ 1  │ Software Dev │────│ 1  │Sophia│   1    │
│ 2  │ Mathematics  │────│ 2  │Henry │   1    │
│ 3  │ Physics      │ ✗  │ 3  │Emma  │   2    │
└────┴──────────────┘    │ 4  │John  │  NULL  │✗
                         └────┴──────┴────────┘

        ↓ FULL OUTER JOIN ↓

Результат (ВСІ з обох таблиць)
┌──────────────┬─────────┐
│ Department   │ Teacher │
├──────────────┼─────────┤
│ Software Dev │ Sophia  │
│ Software Dev │ Henry   │
│ Mathematics  │ Emma    │
│ Physics      │ NULL    │ ← Кафедра БЕЗ викладачів
│ NULL         │ John    │ ← Викладач БЕЗ кафедри
└──────────────┴─────────┘

5. Порівняння INNER vs OUTER JOINs

5.1. Візуальне порівняння

SELECT D.Name, T.FirstName
FROM Departments AS D
INNER JOIN Teachers AS T
    ON D.Id = T.DepartmentId;

Результат: Тільки збіги (3 рядки)

NameFirstName
Software DevSophia
Software DevHenry
MathematicsEmma

5.2. Порівняльна таблиця

JOIN типЛіва таблицяПрава таблицяNULL можливий?
INNERТільки збігиТільки збіги❌ Ні
LEFTВСІ рядкиТільки збіги✅ Так (права)
RIGHTТільки збігиВСІ рядки✅ Так (ліва)
FULLВСІ рядкиВСІ рядки✅ Так (обидві)

6. Обробка NULL значень

6.1. Проблема NULL в результатах

SELECT
    D.Name AS Department,
    T.FirstName AS Teacher
FROM Departments AS D
LEFT JOIN Teachers AS T
    ON D.Id = T.DepartmentId;

-- Результат містить NULL для кафедр без викладачів

6.2. COALESCE - заміна NULL

SELECT
    D.Name AS Department,
    COALESCE(T.FirstName, 'Немає викладачів') AS Teacher
FROM Departments AS D
LEFT JOIN Teachers AS T
    ON D.Id = T.DepartmentId;

Результат:

DepartmentTeacher
MathematicsEmma
PhysicsНемає викладачів
Software DevSophia
Software DevHenry

6.3. ISNULL - альтернатива COALESCE

SELECT
    D.Name AS Department,
    ISNULL(T.FirstName, 'N/A') AS Teacher
FROM Departments AS D
LEFT JOIN Teams AS T
    ON D.Id = T.DepartmentId;
COALESCE vs ISNULL:
  • COALESCE - стандарт SQL, може приймати багато аргументів
  • ISNULL - T-SQL специфічний, тільки 2 аргументи, трохи швидший

6.4. CASE для складної логіки

SELECT
    D.Name AS Department,
    CASE
        WHEN T.FirstName IS NULL THEN 'Вакансія'
        ELSE T.FirstName + ' ' + T.LastName
    END AS Teacher
FROM Departments AS D
LEFT JOIN Teachers AS T
    ON D.Id = T.DepartmentId;

7. Практичні сценарії OUTER JOIN

7.1. Знайти кафедри БЕЗ викладачів

SELECT D.Name AS EmptyDepartment
FROM Departments AS D
LEFT JOIN Teachers AS T
    ON D.Id = T.DepartmentId
WHERE T.Id IS NULL;  -- Немає викладачів

Логіка: LEFT JOIN дає NULL для T.Id, якщо немає збігу.

7.2. Знайти студентів БЕЗ групи

SELECT
    S.FirstName,
    S.LastName,
    'Не зарахований до групи' AS IssueStatus
FROM Students AS S
LEFT JOIN Groups AS G
    ON S.GroupId = G.Id
WHERE G.Id IS NULL;

7.3. Знайти групи БЕЗ студентів

SELECT
    G.Name AS EmptyGroup,
    COUNT(S.Id) AS StudentsCount  -- Буде 0
FROM Groups AS G
LEFT JOIN Students AS S
    ON G.Id = S.GroupId
GROUP BY G.Name
HAVING COUNT(S.Id) = 0;

7.4. Аудит даних (знайти проблеми)

-- Знайти ВСІ невідповідності між таблицями
SELECT
    CASE
        WHEN D.Id IS NULL THEN 'Викладач без кафедри'
        WHEN T.Id IS NULL THEN 'Кафедра без викладачів'
        ELSE 'OK'
    END AS Status,
    COALESCE(D.Name, 'N/A') AS Department,
    COALESCE(T.FirstName + ' ' + T.LastName, 'N/A') AS Teacher
FROM Departments AS D
FULL OUTER JOIN Teachers AS T
    ON D.Id = T.DepartmentId
WHERE D.Id IS NULL OR T.Id IS NULL;  -- Тільки проблеми

7.5. Звіт з опціональними даними

-- Показати ВСІ предмети з викладачами (якщо є)
SELECT
    S.Name AS Subject,
    COALESCE(T.FirstName + ' ' + T.LastName, 'Наразі не призначено') AS Teacher
FROM Subjects AS S
LEFT JOIN TeachersSubjects AS TS
    ON S.Id = TS.SubjectId
LEFT JOIN Teachers AS T
    ON TS.TeacherId = T.Id
ORDER BY S.Name;

8. LEFT JOIN з агрегацією

8.1. Підрахунок кількості

-- Кількість студентів у кожній групі (включаючи порожні)
SELECT
    G.Name AS GroupName,
    COUNT(S.Id) AS StudentsCount  -- NULL не враховуються!
FROM Groups AS G
LEFT JOIN Students AS S
    ON G.Id = S.GroupId
GROUP BY G.Name
ORDER BY StudentsCount DESC;

Результат:

GroupNameStudentsCount
31PR115
32PR313
33PR510
COUNT(S.Id) повертає 0 для групи без студентів, бо NULL не враховуються!

8.2. Середнє значення з NULL handling

-- Середній бал по кафедрах (включаючи кафедри без оцінок)
SELECT
    D.Name AS Department,
    AVG(A.Grade) AS AvgGrade,
    COUNT(A.Id) AS GradesCount
FROM Departments AS D
LEFT JOIN Teachers AS T
    ON D.Id = T.DepartmentId
LEFT JOIN Assessments AS A
    ON T.Id = A.TeacherId
GROUP BY D.Name
ORDER BY AvgGrade DESC;

9. Множинні OUTER JOINs

9.1. Ланцюг LEFT JOINs

-- Показати ВСІ факультети → групи → студентів
SELECT
    F.Name AS Faculty,
    COALESCE(G.Name, 'Немає груп') AS GroupName,
    COALESCE(S.FirstName + ' ' + S.LastName, 'Немає студентів') AS Student
FROM Faculties AS F
LEFT JOIN Groups AS G
    ON F.Id = G.FacultyId
LEFT JOIN Students AS S
    ON G.Id = S.GroupId
ORDER BY F.Name, G.Name, S.LastName;
Порядок має значення: Якщо зробити Groups RIGHT JOIN Faculties, результат буде інший!

9.2. Комбінація INNER + LEFT JOINs

-- Показати студентів з групами (обов'язково) та оцінками (опціонально)
SELECT
    S.FirstName,
    G.Name AS GroupName,        -- INNER JOIN (обов'язково)
    COALESCE(AVG(A.Grade), 0) AS AvgGrade  -- LEFT JOIN (опціонально)
FROM Students AS S
INNER JOIN Groups AS G          -- Тільки студенти З групою
    ON S.GroupId = G.Id
LEFT JOIN Assessments AS A      -- Оцінки можуть бути відсутні
    ON S.Id = A.StudentId
GROUP BY S.FirstName, G.Name;

10. Best Practices

1. LEFT JOIN замість RIGHT

-- ✅ Використовуйте LEFT
FROM Main AS M
LEFT JOIN Optional AS O

-- ❌ Уникайте RIGHT
FROM Optional AS O
RIGHT JOIN Main AS M

2. NULL handling

-- Завжди обробляйте NULL
COALESCE(column, 'default')
ISNULL(column, 'default')

3. IS NULL для фільтрів

-- Знайти рядки БЕЗ збігу
WHERE T.Id IS NULL

-- ❌ НЕ
WHERE T.Id = NULL  -- Не працює!

4. COUNT NULL-safe

-- COUNT(column) не враховує NULL
COUNT(S.Id)  -- ✅ Правильно

-- COUNT(*) враховує всі рядки
COUNT(*)  -- ⚠️ Включає NULL

11. Практичні завдання


Резюме

Ключові моменти OUTER JOINs:
  1. LEFT JOIN: Всі рядки з лівої таблиці + збіги
  2. RIGHT JOIN: Всі рядки з правої таблиці + збіги (рідко використовується)
  3. FULL OUTER JOIN: Всі рядки з обох таблиць
  4. NULL handling: Завжди обробляйте NULL через COALESCE/ISNULL/CASE
  5. Пошук відсутніх зв'язків: WHERE T.Id IS NULL
  6. COUNT NULL-safe: COUNT(column) не враховує NULL
  7. Best Practice: Використовуйте LEFT JOIN замість RIGHT JOIN
Коли використовувати:
  • Показати всі рядки з головної таблиці
  • Знайти рядки без зв'язків (порожні групи, студенти без оцінок)
  • Аудит даних (знайти проблеми)
  • Звіти з опціональними полями

Попередня тема: INNER JOIN
Наступна тема: CROSS та SELF JOINs

Copyright © 2026