Условие

Есть три таблицы:
students (student_id TEXT, full_name, city, signup_date DATE).
enrollments (enroll_id, student_id, course, enroll_date, progress_pct INT (0–100), status TEXT — completed / in_progress / cancelled, может быть NULL).
reviews (review_id, enroll_id, rating INT 1–5, review_date).
Напишите запрос, который выведет, сколько студентов завершили хотя бы один курс. Завершённым считается обучение со status = 'completed'.
PostgreSQL 15.5. Время — 1 секунда, память — 128 МБ.
Решение
Подход
Один студент мог завершить несколько курсов — нужен COUNT(DISTINCT student_id). Не нужны JOIN-ы — данные есть в enrollments.
Реализация
SELECT COUNT(DISTINCT student_id) AS students_completed
FROM enrollments
WHERE status = 'completed';Альтернатива через подзапрос (читабельнее в больших запросах)
SELECT COUNT(*) AS students_completed
FROM (
SELECT DISTINCT student_id
FROM enrollments
WHERE status = 'completed'
) t;Сложность
O(n) на сканировании enrollments + сортировка/хеш для DISTINCT. На таблице с миллионами строк лучше иметь индекс по (status, student_id).
Подводные камни
COUNT(student_id)безDISTINCT. Посчитает количество завершённых обучений, а не уникальных студентов. Если Иванов завершил 3 курса — будет +3 вместо +1.status = 'completed'противIS NOT NULL. Условие задачи — именно'completed', а не «не отменён». Студент сin_progressне считается завершившим.statusможет бытьNULL.WHERE status = 'completed'корректно отбрасываетNULL-ы (трёхзначная логика). Но если случайно сравнитьstatus <> 'cancelled',NULL-ы тоже отвалятся (а вы могли их хотеть).- Регистр статуса. PostgreSQL чувствителен к регистру:
'Completed'≠'completed'. Если данные грязные —LOWER(status) = 'completed', но это блокирует индекс.
Эталонный ответ
SELECT COUNT(DISTINCT student_id)
FROM enrollments
WHERE status = 'completed';