Чему равно выражение NULL = NULL в PostgreSQL?
показать ответ
Ответ: NULL — «неизвестно». NULL означает «значение неизвестно», поэтому любое сравнение с ним даёт NULL: в WHERE такая строка не пройдёт, ведь условие не стало истинным.
// interview
Пятнадцать вопросов, которые задают аналитикам и разработчикам: сначала попробуй ответить сам, потом раскрывай ответ — под каждым короткий разбор, почему так. Вопросы взяты из банка скилл-тестов Koddo, того же, по которому платформа оценивает уровень.
Ответ: NULL — «неизвестно». NULL означает «значение неизвестно», поэтому любое сравнение с ним даёт NULL: в WHERE такая строка не пройдёт, ведь условие не стало истинным.
Ответ: ILIKE сравнивает без учёта регистра. ILIKE — это регистронезависимый LIKE (расширение PostgreSQL): шаблон «анна» совпадёт со значением «Анна».
Ответ: Только те строки, для которых нашлась пара в обеих таблицах. INNER JOIN оставляет лишь совпавшие пары, а строки без пары отбрасывает — сохранить их умеет LEFT JOIN.
Ответ: SELECT → FROM → WHERE → ORDER BY → LIMIT. Это обязательный синтаксический порядок: если поменять местами WHERE и ORDER BY, запрос не разберётся и упадёт с синтаксической ошибкой.
SELECT customer_id, count(*)
FROM orders
WHERE count(*) > 1
GROUP BY customer_id; Ответ: WHERE отбирает строки до группировки, когда агрегат ещё не посчитан: фильтр по агрегату ставят в HAVING. WHERE работает раньше GROUP BY, когда групп ещё не существует; условие на агрегат ставится в HAVING, которое выполняется уже после группировки.
SELECT c.name
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE ???; Ответ: o.id IS NULL. После LEFT JOIN у клиента без пары все колонки orders равны NULL, поэтому проверка o.id IS NULL и есть анти-джойн; count в WHERE использовать нельзя.
Ответ: 1500: NULL не участвует в расчёте. Агрегатные функции пропускают NULL: avg делит сумму 3000 на количество НЕ-NULL значений (два), а не на общее число строк.
SELECT count(*)
FROM orders
WHERE created_at BETWEEN '2026-06-01' AND '2026-06-30'; Ответ: Дата '2026-06-30' превращается в полночь 2026-06-30 00:00:00, поэтому весь последний день отсекается. Дата без времени трактуется как полночь, и всё, что произошло позже 00:00 последнего дня, выпадает из диапазона; надёжный приём — полуоткрытый интервал created_at >= '2026-06-01' AND created_at < '2026-07-01'.
Ответ: В ON оно фильтрует только правую таблицу, и клиенты без оплаченных заказов остаются в выдаче с NULL. Условие в ON участвует в поиске пары и не убивает строки левой таблицы, а WHERE применяется уже к результату соединения и отбрасывает строки с NULL — именно поэтому LEFT JOIN «превращается» в INNER.
Ответ: UNION убирает дубликаты строк, а UNION ALL возвращает всё как есть. UNION дедуплицирует результат и потому обычно дороже, а UNION ALL просто склеивает выдачи; одинаковое число колонок совместимых типов нужно обоим.
Ответ: ROWS отсчитывает физические строки, а RANGE — логический диапазон значений ключа ORDER BY, поэтому строки с равным ключом попадают в рамку вместе. ROWS оперирует позициями строк («две строки назад»), а RANGE — значениями ключа сортировки, из-за чего все ровесники с одинаковым ключом обрабатываются как единое целое.
Ответ: NOT EXISTS проверяет только факт наличия строки и не сравнивает значения с NULL, поэтому NULL в подзапросе не обнуляет результат. EXISTS отвечает на вопрос «нашлась ли строка», а не «равно ли значение», поэтому трёхзначная логика NULL не превращает выдачу в пустую, как это делает NOT IN.
Ответ: SELECT DISTINCT ON (customer_id) * FROM orders ORDER BY customer_id, created_at DESC;. DISTINCT ON оставляет первую строку каждой группы, а «первую» определяет ORDER BY — поэтому он обязан начинаться с тех же выражений, что стоят в DISTINCT ON (иначе PostgreSQL выдаст ошибку), и только потом идёт created_at DESC.
SELECT id
FROM orders
WHERE row_number() OVER (ORDER BY created_at DESC) = 1; Ответ: Оконные функции вычисляются после WHERE, поэтому в самом WHERE их ещё не существует — нужен подзапрос или CTE. Оконные функции считаются уже после WHERE, GROUP BY и HAVING, поэтому фильтр по ним выносят на уровень выше — в подзапрос или CTE, где колонка с номером строки уже посчитана.
Ответ: FROM → WHERE → GROUP BY → HAVING → оконные функции → SELECT → ORDER BY → LIMIT. Именно этот порядок объясняет три классические ошибки разом: агрегат нельзя фильтровать в WHERE, псевдоним из SELECT недоступен в WHERE, а оконную функцию нельзя положить ни в WHERE, ни в HAVING.