Собесов

Реляционная алгебра: ID студентов, идущих на пересдачу

SQLРеляционная модельСредняяMiddle

Условие

Даны два отношения:

  • PE(StudentID, Subject, Grade) — оценки по физкультуре
  • English(StudentID, Subject, Grade) — оценки по английскому

Найти ID студентов, которые придут на пересдачу хотя бы по одному из предметов (оценка < 3).

Решение

Реляционная алгебра

π_StudentID (σ_(Grade < 3) (PE))
  ∪
π_StudentID (σ_(Grade < 3) (English))

Сначала отбираем (σ) строки с двойками, проектируем (π) на ID, объединяем (∪).

SQL

SELECT StudentID FROM PE      WHERE Grade < 3
UNION
SELECT StudentID FROM English WHERE Grade < 3;

UNION уберёт дубликаты — если студент завалил оба предмета, его ID появится один раз.

Если нужно «все ID, по разу за каждый провал»

SELECT StudentID FROM PE      WHERE Grade < 3
UNION ALL
SELECT StudentID FROM English WHERE Grade < 3;

Если нужно «провалили оба»

-- INTERSECT — пересечение
SELECT StudentID FROM PE      WHERE Grade < 3
INTERSECT
SELECT StudentID FROM English WHERE Grade < 3;

Через UNION ALL + GROUP BY

Альтернатива INTERSECT без него (для СУБД, не поддерживающих):

SELECT StudentID
FROM (
  SELECT StudentID FROM PE      WHERE Grade < 3
  UNION ALL
  SELECT StudentID FROM English WHERE Grade < 3
) t
GROUP BY StudentID
HAVING COUNT(DISTINCT 1) >= 2;  -- хотя бы из двух источников

Либо проще через FULL JOIN.

Кому где живётся легче

Подход Плюсы Минусы
UNION Самое короткое, читается Скрытый DISTINCT — тормозит на больших объёмах
UNION ALL + GROUP BY Контроль Длиннее
Подзапрос с OR в JOIN Подходит, когда столбцы разные Сложнее писать

Подводные камни

  1. UNION vs UNION ALL — первое дедуплицирует. Второе быстрее.
  2. Только StudentID — не выбирайте *, иначе строки не дедуплицируются (Subject разный).
  3. Граница: Grade < 3 — это 1 и 2. <= 2 эквивалентно для целочисленных оценок.
  4. NULL Grade — попадёт в выборку или нет? NULL < 3 это UNKNOWN, не TRUE — не попадёт. Если NULL значит «нет оценки = провал», нужно Grade IS NULL OR Grade < 3.

Эталонный ответ

π_StudentID (σ_(Grade<3) (PE)) ∪ π_StudentID (σ_(Grade<3) (English))
SELECT StudentID FROM PE      WHERE Grade < 3
UNION
SELECT StudentID FROM English WHERE Grade < 3;

Хочешь увидеть разбор?

Зарегистрируйся бесплатно — откроется развёрнутое решение этой задачи и ещё 4 на выбор.

Зарегистрироваться и увидеть разбор
Уже есть аккаунт? Войти