0Pricing
SQL Academy · Урок

NULL в агрегатах и объединениях

Узнайте, как NULL ведёт себя в COUNT, SUM и JOIN

«NULL в агрегатах и объединениях» — бесплатный урок SQL Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.

NULL меняет арифметику

Агрегатные функции и соединения по-особому работают со значением NULL. Если не знать этих правил, итоговые суммы и количества могут незаметно оказаться неверными.

В этом уроке показано, как COUNT, SUM, AVG, GROUP BY и внешние соединения взаимодействуют с отсутствующими значениями.

SELECT amount FROM payments;
-- amount
-- -------
--    100
--   NULL   <- missing
--    200

Агрегатные функции игнорируют NULL

Большинство агрегатных функций — SUM, AVG, MIN, MAX — просто пропускают значения NULL. Они обрабатывают только строки, содержащие данные.

Поэтому NULL в сумме не мешает работе SUM: такое значение просто не включается в итог.

-- Using amounts 100, NULL, 200
SELECT
  SUM(amount) AS total,  -- 300 (NULL skipped)
  MIN(amount) AS lo,     -- 100
  MAX(amount) AS hi      -- 200
FROM payments;

AVG тоже пропускает NULL

AVG делит сумму значений, не равных NULL на количество значений, не равных NULL. NULL исключается из обеих частей вычисления.

Это важно: среднее для набора {100, NULL, 200} равно 150, а не 100 — NULL не считается нулём.

-- (100 + 200) / 2 = 150, the NULL row is ignored
SELECT AVG(amount) AS avg_amount FROM payments;

-- If you WANT NULLs counted as 0, COALESCE first:
SELECT AVG(COALESCE(amount, 0)) AS avg_with_zeros FROM payments; -- 100

COUNT(*) и COUNT(column)

Это различие часто приводит к ошибкам:

  • COUNT(*) считает строки, включая строки со значениями NULL.
  • COUNT(column) считает только строки, в которых этот столбец не равен NULL.
-- 3 rows total, but only 2 have a non-NULL amount
SELECT
  COUNT(*)       AS row_count,    -- 3
  COUNT(amount)  AS has_amount    -- 2
FROM payments;

COUNT(DISTINCT) и NULL

COUNT(DISTINCT col) считает количество различных значений, не равных NULL. NULL полностью исключаются — они никогда не увеличивают количество различных значений.

Помните об этом, когда подсчитываете, «сколько существует уникальных X».

-- statuses: 'paid', NULL, 'paid', 'void'
SELECT COUNT(DISTINCT status) AS distinct_statuses
FROM payments;
-- 2  (paid, void) -- NULL not counted

Агрегаты для пустого набора

Когда агрегатная функция работает с нулём строк, результат зависит от функции:

  • COUNT(...) возвращает 0.
  • SUM, AVG, MIN, MAX возвращают NULL.

Используйте COALESCE, чтобы при необходимости превратить NULL в сумме в 0.

-- No rows match -> SUM is NULL, not 0
SELECT COALESCE(SUM(amount), 0) AS total
FROM payments
WHERE status = 'refunded';  -- no such rows

GROUP BY объединяет NULL в одну группу

Хотя в других случаях NULL = NULL даёт неизвестный результат, GROUP BY помещает все NULL в одну группу.

Таким образом, категория NULL становится отдельной группой в результатах, и строки с отсутствующими данными можно суммировать вместе.

SELECT category, COUNT(*) AS n
FROM products
GROUP BY category;

-- category | n
-- ---------+---
-- books    | 5
-- toys     | 3
-- NULL     | 2   <- all NULL categories in one group

NULL во внешних соединениях

Внешние соединения — один из главных источников NULL. LEFT JOIN сохраняет каждую строку из левой таблицы; если справа нет совпадения, столбцы правой таблицы становятся равны NULL.

Эти NULL означают «нет совпадающей строки», а не «сохранённое значение NULL».

SELECT c.name, o.id AS order_id
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id;

-- name  | order_id
-- ------+---------
-- Alice | 10
-- Bob   | NULL     <- Bob has no orders

Подсчёт совпадений после LEFT JOIN

Чтобы после LEFT JOIN посчитать только реальные совпадения, подсчитывайте столбец правой таблицы, в котором нет NULL, а не COUNT(*).

COUNT(o.id) игнорирует строки со значением NULL, созданные для несовпавших строк левой таблицы, и возвращает настоящее количество заказов.

SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name;

-- Bob shows 0, not 1×NULL

Фильтрация несовпадений

Важный нюанс: условие для правой таблицы в WHERE, добавленное после LEFT JOIN, превращает его во внутреннее соединение, поскольку NULL = value даёт неизвестный результат и такая строка отфильтровывается.

Если нужно сохранить несовпавшие строки, поместите условие в предложение ON или явно проверьте наличие NULL.

-- Accidentally drops Bob (his o.status is NULL)
SELECT c.name FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'open';

-- Keep unmatched rows: move the test into ON
SELECT c.name FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id AND o.status = 'open';

Практические правила

Применяйте эти правила в каждом запросе с агрегатными функциями и соединениями:

  • Агрегатные функции игнорируют NULL (кроме COUNT(*)).
  • COUNT(col) < COUNT(*), если в столбце col есть NULL.
  • SUM/AVG для пустого набора возвращают NULL — оборачивайте их в COALESCE.
  • LEFT JOIN создаёт NULL для несовпадений; подсчитывайте ключ правой таблицы.
  • Фильтры по правой таблице должны находиться в ON, а не в WHERE.
SELECT c.name,
  COALESCE(SUM(o.amount), 0) AS spent,
  COUNT(o.id)                AS orders
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name;

Быстрая проверка

В столбце amount в трёх строках находятся значения 100, NULL и 200. Что вернут COUNT(*) и COUNT(amount)?

Итоги

Вы узнали, как NULL распространяются при агрегации и соединениях: агрегатные функции пропускают NULL, COUNT(*) считает строки, а COUNT(col) — значения, не равные NULL; суммы для пустого набора равны NULL, а GROUP BY объединяет NULL в одну группу.

Вы также увидели, что внешние соединения создают NULL для несовпадений, и узнали, почему фильтры по правой таблице должны находиться в ON. На этом завершается курс Работа с NULL — теперь вы умеете уверенно обрабатывать отсутствующие данные.

-- A NULL-safe summary query
SELECT c.name,
  COUNT(o.id)                AS orders,
  COALESCE(SUM(o.amount), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name
ORDER BY total_spent DESC;

Часто задаваемые вопросы

Урок «NULL в агрегатах и объединениях» бесплатный?

Да — полный текст урока «NULL в агрегатах и объединениях» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.

Чему я научусь в уроке «NULL в агрегатах и объединениях»?

Узнайте, как NULL ведёт себя в COUNT, SUM и JOIN Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать SQL Academy?

Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «NULL в агрегатах и объединениях»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке SQL Academy?

Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Что на самом деле означает NULL
  2. IS NULL и IS NOT NULL
  3. COALESCE и NULLIF
  4. NULL в агрегатах и объединениях
← Назад к SQL Academy