Возврат NULL при отсутствии N-го значения
Разбирайте любимый интервьюерами пограничный случай: корректно обрабатывайте слишком малое число строк.
«Возврат NULL при отсутствии N-го значения» — бесплатный урок SQL Interview Prep на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Interview Prep, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Interview Prep содержит 4 уроков всего.
Пограничный случай, который любят спрашивать на собеседованиях
После того как Вы правильно составили запрос для N-го по величине значения, интервьюер добавляет: «Что, если в таблице меньше N различных зарплат? Мне нужна одна строка с NULL, а не пустой результат».
Этот вопрос отделяет кандидатов, заучивших запрос, от тех, кто понимает поведение набора результатов. Многие решения незаметно возвращают ноль строк вместо одной строки, содержащей NULL.
Весь этот урок посвящён тому, как принудительно получить ровно одну результирующую строку, значение которой равно NULL, если N-го значения не существует.
Почему одного DENSE_RANK недостаточно
Вспомните стандартный запрос для N-го по величине значения. Если есть только две различные зарплаты и Вы запрашиваете третью, условие WHERE rnk = 3 ничего не находит, поэтому запрос возвращает пустой набор: ноль строк.
Пустой набор — не то же самое, что строка, содержащая NULL. Если в требованиях сказано «вернуть NULL», пустой результат не пройдёт проверку, даже если базовая логика верна.
SELECT salary
FROM (
SELECT salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employee
) t
WHERE rnk = 3; -- returns NO rows if fewer than 3 distinct salariesИсправление 1: обернуть запрос во внешний SELECT
Самое простое надёжное исправление: сделать весь запрос для N-го по величине значения скалярным подзапросом внутри одного SELECT. Скалярный подзапрос, не нашедший строк, вычисляется как NULL, а внешний SELECT всегда создаёт ровно одну строку.
Это канонический ответ для варианта в стиле LeetCode с требованием «вернуть NULL», и он работает в любом диалекте.
SELECT (
SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 2 -- N = 3
) AS third_highest;Почему работает приём со скалярным подзапросом
Нужное поведение обеспечивают два правила:
- Скалярный подзапрос должен возвращать не более одного значения. Если он не возвращает строк, SQL подставляет
NULL. - Внешний SELECT без
FROM(или с источником из одной строки) всегда выдаёт ровно одну строку.
Поэтому, если внутренний запрос находит N-е значение, Вы получаете его; если ничего не находит — одну строку со значением NULL. Именно это и требовалось в условии собеседования.
Исправление 1 с вариантом на DENSE_RANK
Такая же оболочка работает и для решения с оконной функцией. Поместите ранжированный запрос внутрь скалярного подзапроса; если ни одна строка не имеет ранга N, подзапрос выдаёт NULL, а внешний SELECT всё равно возвращает одну строку.
SELECT (
SELECT salary
FROM (
SELECT salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employee
) t
WHERE rnk = 3
) AS third_highest;Исправление 2: MAX автоматически возвращает NULL
Вспомните идею с вложенными MAX из первого урока. Агрегат над нулём строк возвращает NULL и при этом всё равно создаёт одну строку. Для второго по величине значения это краткое решение, которое сразу выполняет требование о NULL.
Недостаток в том, что расширять чистое вложение MAX до произвольного N становится неудобно, поэтому такой вариант лучше всего подходит именно для второго по величине значения.
SELECT MAX(salary) AS second_highest
FROM employee
WHERE salary < (SELECT MAX(salary) FROM employee);Исправление 3: COALESCE с резервным значением
Если Ваша среда гарантирует наличие одной строки, но значение может отсутствовать по другой причине, можно обернуть результат в COALESCE, чтобы указать явное значение по умолчанию.
Заметьте: COALESCE помогает только после появления строки. Он не превращает пустой набор результатов в строку. Поэтому объедините его со скалярным подзапросом-оболочкой, который гарантирует наличие строки, а затем примените COALESCE к значению, если хотите получить не NULL, а, например, 0.
SELECT COALESCE((
SELECT DISTINCT salary
FROM employee
ORDER BY salary DESC
LIMIT 1 OFFSET 2
), 0) AS third_highest_or_zero;Что не исправляет проблему
Остерегайтесь исправлений, которые выглядят правильно, но не работают:
- Добавление
COALESCEнепосредственно вокруг запроса, возвращающего ноль строк, ничего не даёт: дляCOALESCEнет строки, к которой можно примениться. IFNULL/ISNULLимеют то же ограничение, что иCOALESCE.- Добавление
LIMIT 1не создаёт строку, если ни одна строка не подошла.
Проблему с количеством строк нужно решать с помощью скалярного подзапроса-оболочки или агрегата, а не одними функциями подстановки NULL.
Практический пример: запрос третьего значения из двух
Зарплаты: 500, 500, 300. Различных зарплат всего две — 500 и 300, поэтому третьей по величине нет.
- Обычный DENSE_RANK с условием WHERE rnk = 3: возвращает ноль строк. Требование не выполнено.
- Оболочка со скалярным подзапросом: внутренний запрос ничего не находит, поэтому внешний SELECT возвращает одну строку:
NULL. Требование выполнено. - COALESCE(..., 0): возвращает одну строку со значением
0, если было запрошено числовое значение по умолчанию.
Как объяснить это на собеседовании
Получайте дополнительные баллы, проговаривая ход рассуждений:
- «Наивный запрос возвращает пустой набор, а не NULL, поэтому я оберну его в скалярный подзапрос, чтобы гарантировать одну строку».
- «Скалярный подзапрос без совпавших строк вычисляется как NULL — именно это и требуется».
- «Если вместо NULL Вы предпочитаете значение по умолчанию, например 0, я добавлю вокруг подзапроса COALESCE».
Суть этого вопроса — показать, что Вы понимаете разницу между количеством строк и семантикой значений.
Объединяем всё вместе
Надёжное параметризуемое решение для N-го по величине значения или NULL: ранжируйте различные зарплаты, отфильтруйте ранг N внутри скалярного подзапроса и позвольте внешнему SELECT гарантировать наличие одной строки.
Этот запрос обрабатывает дубликаты с помощью DENSE_RANK, обобщается на любое N и корректно возвращает NULL, когда N превышает количество различных зарплат.
SELECT (
SELECT salary
FROM (
SELECT salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS rnk
FROM employee
) t
WHERE rnk = :n
LIMIT 1
) AS nth_highest;Быстрая проверка
Рассуждайте о разнице между количеством строк и значениями NULL.
Итоги
Когда N превышает количество доступных различных зарплат, обычный запрос с ранжированием возвращает пустой набор, а не NULL.
- Оберните запрос для N-го по величине значения в скалярный подзапрос внутри внешнего SELECT, чтобы он всегда создавал одну строку и выдавал
NULL, если подходящего значения нет. - Вариант с MAX с вложенным MAX автоматически возвращает
NULLдля случая со вторым по величине значением. - COALESCE подставляет значение только после появления строки; он не превращает ноль строк в одну.
Если интервьюер спрашивает о корректной обработке NULL, всегда различайте количество строк и значение.
Часто задаваемые вопросы
Урок «Возврат NULL при отсутствии N-го значения» бесплатный?
Да — полный текст урока «Возврат NULL при отсутствии N-го значения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Interview Prep, подпишись на CoddyKit PRO. Курс SQL Interview Prep содержит 4 уроков всего.
Чему я научусь в уроке «Возврат NULL при отсутствии N-го значения»?
Разбирайте любимый интервьюерами пограничный случай: корректно обрабатывайте слишком малое число строк. Ты практикуешь SQL Interview Prep с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать SQL Interview Prep?
Предыдущий опыт не требуется. SQL Interview Prep на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.
Сколько времени занимает урок «Возврат NULL при отсутствии N-го значения»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке SQL Interview Prep?
Да. Каждый урок SQL Interview Prep включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Вторая по величине зарплата: пять способов
- N-е по величине значение с DENSE_RANK
- Самый высокооплачиваемый сотрудник отдела
- Возврат NULL при отсутствии N-го значения