Подзапросы в SQL: EXISTS и NOT IN
«Он тратил подозрительно много» — красивая фраза, бесполезная без числа. Сколько это — «много»? Порог не висит на стене. Его сперва нужно вычислить, и только потом с ним сравнивать.
Скалярный подзапрос считает одно число и подставляет его прямо в сравнение — норму, с которой всё остальное сверяется.
SELECT *
FROM receipts
WHERE amount > (SELECT AVG(amount) FROM receipts);А если норма у каждого своя?
Это средняя сумма по всем — общий фон. Но норма бывает своя у каждого. Коррелированный подзапрос пересчитывает порог заново для каждой строки, глядя только на её окружение.
SELECT l.*
FROM receipts l
WHERE l.amount > (
SELECT AVG(l2.amount)
FROM receipts l2
WHERE l2.person_id = l.person_id
);«„Много“ у скупщика и „много“ у курьера — два разных числа. Норму считают под человека, не под весь город разом.»
EXISTS не считает норму — он спрашивает короче: «есть ли вообще след». Останавливается на первом совпадении, не собирая список целиком.
SELECT *
FROM contacts p
WHERE EXISTS (
SELECT 1 FROM receipts l WHERE l.person_id = p.id
);Почему запрос молчит, хотя ошибки нет?
А теперь капкан-бомба, и он ждёт ровно тех, кто ищет обратное — «у кого следа нет вообще». NOT IN с подзапросом, в котором затесался хотя бы один NULL, отдаёт пустоту всегда, даже когда таких людей полно.
SELECT * FROM contacts
WHERE id NOT IN (SELECT person_id FROM receipts);
-- если в receipts.person_id есть хоть один NULL —
-- результат: ноль строк, всегда, даже если ответ должен быть длиннымСравнение с NULL не даёт ни ДА, ни НЕТ — оно даёт «неизвестно», и NOT IN на неизвестном никогда не срабатывает. Лечится не осторожностью, а сменой инструмента: NOT EXISTS про NULL не спотыкается вовсе.
SELECT * FROM contacts p
WHERE NOT EXISTS (
SELECT 1 FROM receipts l WHERE l.person_id = p.id
);«Запрос отработал, ошибку никто не бросил, строк — ноль. Тишина здесь не значит „чисто“. Она значит: спросили не то.»
На работе так считают динамический порог — «выше среднего по своей группе», а не по всем разом — и так ищут «у кого совсем нет записи» без риска молча получить пустой отчёт.
Норма у каждого своя — на этом в архиве держится целое дело. Попробуйте посчитать порог сами, а не поверить чужому числу.