← Все курсы · Курс SQL

COUNT с CASE WHEN: подсчет некоторых строк без фильтрации остальных

Майкл Носито · Обновлено в августе 2026 г. · Каждый запрос на этой странице был выполнен до его публикации.

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

Что вы делаете: COUNT(CASE WHEN condition THEN 1 END), без ELSE. Для восьми заказов ниже это возвращает 5 отправленных. Добавлять ELSE 0 и тот же запрос возвращает 8.

Краткая версия. COUNT подсчитывает значения и игнорирует NULL. Этот единственный факт и составляет всю технику.

Стол

CREATE TABLE orders(order_id INT, customer_id INT, amount INT, status TEXT);
INSERT INTO orders VALUES
(101,1,120,'shipped'),(102,1,80,'shipped'),(103,2,200,'cancelled'),
(104,2,50,'shipped'),(105,3,300,'shipped'),(106,4,90,'pending'),
(107,4,60,'shipped'),(108,5,150,'cancelled');

Восемь заказов: пять отправлено, два отменены, один находится на рассмотрении. Общая сумма 1050.

Запрос

SELECT
  COUNT(*)                                          AS all_orders,
  COUNT(CASE WHEN status = 'shipped'   THEN 1 END)  AS shipped,
  COUNT(CASE WHEN status = 'cancelled' THEN 1 END)  AS cancelled,
  SUM(CASE WHEN status = 'shipped' THEN amount ELSE 0 END) AS shipped_value
FROM orders;
все_ордерыотправленныйотмененотправленное_значение
852610

Один проход по столу, четыре ответа, и каждый из них уважает то, что WHERE пункт, который вы добавите позже. В этом заключается практическое преимущество перед тремя отдельными запросами: фильтры никогда не разойдутся.

ELSE, которое ломает это

COUNT(CASE WHEN status = 'shipped' THEN 1 END)          -- 5, correct
COUNT(CASE WHEN status = 'shipped' THEN 1 ELSE 0 END)   -- 8, wrong

A CASE без совпадения WHEN и нет ELSE возвращает NULL и COUNT пропускает NULL. Писать ELSE 0 и каждая строка теперь дает ноль, который является значением, поэтому COUNT считает все восемь.

Никаких ошибок, никаких предупреждений, и число по-прежнему правдоподобно. Это наиболее распространенная ошибка при условном агрегировании, и она легко выдерживает проверку, поскольку ELSE 0 выглядит как хорошая оборонительная практика.

написано возвращает Почему
COUNT(CASE WHEN c THEN 1 END)5Несовпадающие строки имеют значение NULL и пропускаются.
COUNT(CASE WHEN c THEN 1 ELSE 0 END)80 является значением и учитывается
SUM(CASE WHEN c THEN 1 ELSE 0 END)5Добавляет по одному за матч
SUM(CASE WHEN c THEN 1 END)5SUM также игнорирует NULL

Трое из этих четырех правы. Рекомендуется выбрать одну форму и использовать ее последовательно, потому что тогда случайное ELSE выглядит неправильно с первого взгляда, вместо того, чтобы выглядеть внимательно.

Чек, который его ловит. Ваши условные подсчеты должны составить общую сумму, как только будет охвачена каждая категория. Здесь 5 отправленных плюс 2 отмененных плюс 1 ожидающий равняются 8. Если условное количество точно равно общему, найдите ELSE.

Цены в том же запросе

SELECT
  COUNT(*) AS orders,
  COUNT(CASE WHEN status = 'shipped' THEN 1 END) AS shipped,
  ROUND(100.0 * COUNT(CASE WHEN status = 'shipped' THEN 1 END) / COUNT(*), 1)
    AS shipped_pct
FROM orders;
-- 8, 5, 62.5

тот 100.0 имеет значение. В базах данных с целочисленным делением 5 / 8 равно 0, и скорость возвращается к нулю для каждой строки. Сначала умножьте на десятичную дробь или отбросьте одну сторону.

Берегите также знаменатель. COUNT(*) не может быть нулевым, если строка существует, но может быть условным знаменателем, а деление на ноль либо приводит к ошибке, либо возвращает NULL в зависимости от механизма. NULLIF(denominator, 0) делает поведение везде одинаковым.

В сочетании с GROUP BY

SELECT customer_id,
       COUNT(*) AS orders,
       COUNT(CASE WHEN status = 'shipped'   THEN 1 END) AS shipped,
       COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled
FROM orders
GROUP BY customer_id
ORDER BY customer_id;

Это форма, которая делает условные подсчеты достойными изучения: одна строка на каждого клиента, несколько показателей по столбцам, отсутствие самосоединений или повторяющихся подзапросов. Это SQL-эквивалент сводной таблицы, и именно так на самом деле создается большинство таблиц отчетов.

Предложение FILTER, где оно у вас есть.

SELECT
  COUNT(*)                                   AS orders,
  COUNT(*) FILTER (WHERE status = 'shipped') AS shipped,
  SUM(amount) FILTER (WHERE status = 'shipped') AS shipped_value
FROM orders;

Стандартный SQL, поддерживаемый PostgreSQL и SQLite. Он говорит именно то, что означает, и не имеет никакого значения. ELSE ошибиться. SQL Server и MySQL его не поддерживают, поэтому CASE форма остается портативным ответом, и в любом случае стоит свободно владеть им.

Как применить это в своей работе

  1. Найдите отчет, построенный на основе нескольких почти идентичных запросов, и сверните его в один с условными агрегатами.
  2. Найдите в сохраненных запросах COUNT(CASE и проверь каждого на наличие ELSE.
  3. Добавьте проверку работоспособности: количество категорий должно суммироваться с общей суммой.
  4. Умножить на 100.0 в каждой ставке и заключим знаменатели в NULLIF.
  5. Использовать FILTER где ваша база данных поддерживает это, и отметьте в комментарии, почему другие запросы этого не поддерживают.

Единственная привычка, которую нужно сохранить

Спросите, что дает несовпадающая строка. NULL и пропускается, или 0 и засчитывается. Каждый условный агрегат в SQL сводится к этому вопросу, и ответ меняется в зависимости от функции, в которую вы его включили.

Есть ли в ваших отчетах условное количество, равное общему количеству строк?

Каждый номер здесь был проведён до того, как был опубликован. Восемь заказов: COUNT без ELSE возвращает 5, с ELSE 0 возвращает 8, стоимость отгрузки — 610, а доля отгрузки — 62,5 процента.
Одна ошибка ELSE превращает условный подсчет в подсчет строк, и никто не жалуется.

SQL для аналитиков — это 458 страниц, которые считывают запросы построчно в повседневных словах, поэтому ошибочный результат нужно где-то отслеживать, а не перепечатывать, пока он не станет выглядеть лучше.

SQL для аналитиков, 19 $ →
Введите запросы, а не просто читайте их.

СЧЕТ в SQL охватывает саму функцию, Выражение CASE покрывает другую половину, и SQL-практикум передает вам один выполняемый запрос за раз.

Открыть SQL-детализацию →