На практике очень редко можно столкнуться с таблицами, содержащие абсолютно чистые и полные данные. Более вероятно, что вы будете работать с таблицами, в которых некоторые значения отсутствуют. В SQL эти отсутствующие данные обозначаются как NULL
. Обработка таких значений — это важный навык для аналитика, потому что неправильное управление NULL
-значениями может привести к искаженным результатам и ошибочным выводам.
Чтобы попрактиковаться в работе с NULL
в SQL, мы использовали нашу базу данных и составили в ней таблицу delivery
, которая содержит следующие столбцы:
- id доставки (
delivery_id
) - id заказа (
order_id
) - дата доставки (
delivery_date
) - адрес доставки (
delivery_address
)
В этой таблице мы оставили пропуски в дате доставки и адресе доставки, чтобы показать, как SQL операторы могут обрабатывать эти NULL
-значения.
Операторы для обработки NULL
в SQL
Для начала познакомимся с оператором COALESCE
. С его помощью можно выбрать первое ненулевое значение из списка. Он принимает два или более аргументов и возвращает первый аргумент, который не равен NULL
. Если все аргументы равны NULL
, COALESCE
возвращает NULL
.
Предположим, мы хотим получить адрес доставки для каждого заказа, но в некоторых случаях адрес доставки отсутствует. Для таких случаев мы можем использовать COALESCE
, чтобы вместо NULL
вывести строку с текстом «Адрес не указан». Вот как это можно сделать:
SELECT
order_id,
COALESCE(delivery_address, 'Адрес не указан') AS delivery_address
FROM delivery;
В этом запросе для каждого заказа из таблицы delivery
мы получаем два параметра — order_id
и delivery_address
. Если delivery_address
равен NULL
, оператор COALESCE
заменяет его на строку 'Адрес не указан'.
Таким образом, даже если в исходных данных есть отсутствующие значения, мы все равно получаем полезную информацию для каждого заказа.
Аналогично оператору COALESCE
, мы можем использовать IFNULL
— получится тот же результат. Оператор IFNULL
принимает два аргумента и возвращает первый, если он не NULL
. В противном случае возвращается второй аргумент.
Для приведенного примера с таблицей delivery
запрос будет выглядеть так:
SELECT
order_id,
IFNULL(delivery_address, 'Адрес не указан') AS delivery_address
FROM delivery;
Результат запроса будет совпадать с результатом использования COALESCE
В SQL также есть операторы NVL
, NVL2
и DECODE
. Они также позволяют обрабатывать NULL
-значения, но не поддерживаются в СУБД SQlite. В этом смысле надежнее всего использовать оператор COALESCE
, потому что он поддерживается практически во всех известных реляционных СУБД.
Риски работы с NULL
Существует множество различных нюансов при работе с NULL
значениями, которые всегда следует учитывать.
Для примера попробуем найти всех клиентов, у которых не указан возраст. Если мы используем оператор =
, запрос не вернет никаких результатов, даже если в таблице есть клиенты с возрастом NULL
:
SELECT * FROM customers WHERE age = NULL;
SQL считает, что NULL
не равно ничему, даже другому NULL
. Поэтому age = NULL
всегда возвращает false
, и никакие строки не выбираются.
Вместо этого следует использовать оператор IS NULL
:
SELECT * FROM customers WHERE age IS NULL;
Этот запрос вернет всех клиентов, у которых в столбце age
указано значение NULL
— то есть не указан возраст.
Еще следует помнить, что арифметические операции с NULL
в SQL всегда возвращают NULL
. Например, мы берем таблицу customers
и хотим увеличить customer_id
каждого клиента на единицу:
SELECT
*,
customer_id + 1 AS updated_id
FROM orders;
Если значение customer_id
было NULL
, то updated_id
также будет NULL
, несмотря на прибавление единицы. Это может привести к неожиданным результатам, ведь мы ожидали увеличение на единицу во всех customer_id
. Чтобы с такой проблемой не сталкиваться, можно использовать COALESCE
и заменить NULL
на 0
перед выполнением арифметической операции:
SELECT
*,
COALESCE(customer_id, 0) + 1 AS updated_id
FROM orders;
Еще один неочевидный момент — мы можем случайно нарушить связи между таблицами, если попытаемся соединить данные из двух таблиц, во внешнем ключе которых есть NULL
.
Вернемся к нашей таблице orders
. В ней столбец product_id
является внешним ключом — то есть он ссылается на столбец product_id
в другой таблице products
. При этом у нас есть заказы с product_id = NULL
.
Если мы попытаемся связать данные заказа с данными клиента, то заказы с product_id = NULL
не свяжутся с продуктом:
SELECT *
FROM orders
INNER JOIN products
ON orders.product_id = products.product_id;
В этом запросе заказы с product_id = NULL
не включились в таблицу, потому что их нельзя связать с продуктами по внешнему ключу. Это может привести к потере информации в результатах запроса, если эти заказы важны для анализа.
Чтобы учесть такие заказы, можно использовать LEFT JOIN
вместо INNER JOIN
:
SELECT *
FROM orders
LEFT JOIN products
ON orders.product_id = products.product_id;
Теперь все заказы будут включены в результат, но product_name
будет равен NULL
для заказов, у которых product_id = NULL
.
Также при работе с NULL
следует помнить о нюансах, с которыми мы столкнулись на предыдущих уроках.
Например, NULL
-значения могут появляться в разных местах в результате сортировки в зависимости от СУБД. В некоторых они появляются на вершине при сортировке по возрастанию, а в других — на дне. С помощью операторов NULLS LAST
и NULLS FIRST
мы можем указать явно, где должны располагаться значения NULL
в результирующем наборе данных.
Наконец, при работе с агрегирующими функциями (например, COUNT
, AVG
, SUM
) следует иметь в виду, что они обрабатывают NULL
по-особенному. Всегда сверяйтесь с документацией функции для конкретной базы данных.
Выводы
Подведем итоги и сформулируем правила работы с NULL
-значениями:
- В SQL существуют различные функции и операторы для обработки
NULL
значений, такие какCOALESCE
,IFNULL
,NVL
,NVL2
, иDECODE
. Но не все из них поддерживаются во всех СУБД, поэтому важно знать и уметь использовать универсальные функции, такие какCOALESCE
- Значения
NULL
ведут себя особым образом при выполнении запросов. Например, они не равны ничему (даже другомуNULL
), их арифметические операции всегда возвращаютNULL
, и они могут быть расположены в разных местах при сортировке в зависимости от СУБД. - Если столбец — это внешний ключ, то
NULL
-значения в нем могут нарушать связи между таблицами и приводить к потере информации при выполненииJOIN
-запросов - Агрегирующие функции (
COUNT
,AVG
,SUM
) особенным образом обрабатываютNULL
-значения, что может привести к искажению результатов агрегации
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.