← Все вопросы

Как посчитать количество строк, удовлетворяющих условию, внутри одного запроса (COUNT с условием)?

Задан 24 месяца назад404 просмотров2 ответа
9

Хочу в одном запросе получить и общее число заказов, и сколько из них оплачено. Делать два отдельных COUNT с разными WHERE неудобно. Как посчитать строки по условию в той же выборке, без второго запроса?

2 ответа

13
✓ Принятый ответ — помог автору

Трюк в том, что COUNT считает только не-NULL значения. Если внутри него поставить выражение, которое для нужных строк даёт значение, а для остальных — NULL, получится условный подсчёт.

Универсальный способ — COUNT(CASE ...):

SELECT
  COUNT(*) AS total,
  COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid,
  COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled
FROM orders;

Здесь CASE WHEN status = 'paid' THEN 1 END возвращает 1 для оплаченных и NULL для остальных (ветки ELSE нет — значит NULL), и COUNT их посчитает. Один проход по таблице, несколько счётчиков сразу.

Ещё чаще для «сколько строк подходит» удобнее SUM с условием, потому что условие даёт 1/0:

SELECT
  SUM(CASE WHEN total > 1000 THEN 1 ELSE 0 END) AS big_orders
FROM orders;

В PostgreSQL есть ещё более лаконичный синтаксис FILTER:

SELECT
  COUNT(*) AS total,
  COUNT(*) FILTER (WHERE status = 'paid') AS paid
FROM orders;

Частая ошибка — писать COUNT(status = 'paid'): булево выражение само по себе не NULL, и COUNT посчитает все строки, и true, и false. Нужна именно конструкция через CASE (или FILTER), чтобы ненужные строки превращались в NULL.

5

Это особенно мощно в связке с GROUP BY — так строят сводные отчёты «по строкам категория, по столбцам статусы»:

SELECT
  category,
  COUNT(*) AS total,
  COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid
FROM orders
GROUP BY category;

Получаете по каждой категории и общее число, и число оплаченных рядом. Такой приём называют условной агрегацией или поворотом (pivot) данных.

Ваш ответ

Войдите, чтобы ответить на вопрос.