Как написать SQL-запрос для поиска заказов без существующих пользователей?
Что проверяют
Умеете ли вы писать анти-соединение и знаете ли ловушку с NULL. Для тестировщика это ещё и вопрос о целостности данных: откуда вообще взялись заказы-сироты. Следом обычно спрашивают, почему NOT IN может вернуть пустой результат.
Как отвечать
Сначала уточните условие: считается ли заказ с пустым user_id заказом без пользователя и есть ли в users мягкое удаление вроде deleted_at. От этого зависит запрос.
SELECT o.id, o.user_id
FROM orders o
WHERE NOT EXISTS (
SELECT 1 FROM users u WHERE u.id = o.user_id
);Варианты и их поведение:
NOT EXISTS: самый надёжный, корректно работает сNULL, планировщик превращает его в анти-соединение;LEFT JOIN ... WHERE u.id IS NULL: результат тот же. ПроверятьIS NULLнужно по ключу правой таблицы, а не по колонке, которая может быть пустой сама по себе;NOT IN (SELECT ...): работает, пока в подзапросе нетNULL. Стоит там появиться одномуNULL, условие для всех строк становится неизвестным, и запрос возвращает ноль строк. Заказы сuser_id IS NULLон тоже не покажет.
Если пользователи удаляются мягко, добавьте в подзапрос AND u.deleted_at IS NULL. Для отчёта полезно посчитать масштаб через COUNT(*) и сгруппировать по дате создания заказа, чтобы увидеть, когда сироты начали появляться.
Дальше покажите мышление тестировщика. Нашлись такие заказы, значит есть баг: на orders.user_id нет внешнего ключа, удаление пользователя не каскадирует и не блокируется, или данные залиты в обход приложения. Проверьте схему, а в баг-репорт приложите запрос и число строк.
Где спотыкаются
Пишут NOT IN и не знают про NULL. На тестовой базе запрос возвращает строки, на реальной с одной пустой записью ничего, и проверка проходит зелёной при настоящей проблеме.
Используют INNER JOIN и потом ищут, куда пропали нужные строки. Внутреннее соединение выкидывает как раз заказы без пары.
Находят сирот и на этом останавливаются. Для QA вопрос не закрыт, пока не понятно, как такие записи появились и почему схема этого не запретила.