Kodokon kodokon.com

HAVING и продвинутые агрегации

Фильтруй после агрегации с помощью HAVING и разворачивай данные через CASE WHEN внутри агрегатных функций.

8 мин · 3 вопросов

Открыть этот урок в Kodokon

Ты уже знаешь GROUP BY. Этот модуль разбирает то, что отличает просто корректный запрос от профессионального: фильтрацию после агрегации, условные агрегаты и уверенное понимание порядка вычисления. Чтобы потренироваться, открой sqliteonline.com (движок SQLite) или запусти sqlite3 в терминале: в каждом уроке есть нужные тебе инструкции CREATE TABLE и INSERT. Скопируй этот набор данных, прежде чем запускать примеры.

SQL
CREATE TABLE orders (
  id INTEGER PRIMARY KEY,
  customer TEXT NOT NULL,
  category TEXT NOT NULL,
  amount REAL NOT NULL,
  status TEXT NOT NULL
);

INSERT INTO orders (customer, category, amount, status)
VALUES
  ('alice', 'books', 42.0, 'paid'),
  ('alice', 'games', 55.0, 'paid'),
  ('alice', 'books', 18.5, 'refunded'),
  ('bruno', 'games', 70.0, 'paid'),
  ('bruno', 'books', 12.0, 'paid'),
  ('chloe', 'games', 95.0, 'paid'),
  ('chloe', 'books', 30.0, 'paid'),
  ('david', 'books', 8.0, 'refunded');
Набор данных для этого урока, выполни его первым.

Логический порядок вычисления - вот ключ: FROMWHEREGROUP BYHAVINGSELECTORDER BY. WHERE фильтрует строки до группировки; HAVING фильтрует группы после того, как агрегаты уже посчитаны. Именно поэтому WHERE SUM(amount) > 90 недопустим: на этапе WHERE никакой суммы ещё не существует. Здесь мы оставляем только клиентов, чья оплаченная сумма превышает 90 (ожидаем: alice и chloe).

SQL
SELECT customer,
       COUNT(*) AS nb_orders,
       SUM(amount) AS total
FROM orders
WHERE status = 'paid'
GROUP BY customer
HAVING SUM(amount) > 90;
WHERE убирает строки, HAVING убирает группы.

Второй обязательный паттерн - условный агрегат. Подставив CASE WHEN внутрь SUM или COUNT, ты считаешь сразу несколько метрик за один проход по таблице там, где новичок запустил бы три отдельных запроса. Это стандартный приём, чтобы развернуть строки в колонки.

SQL
SELECT customer,
  SUM(CASE WHEN category = 'books'
      THEN amount ELSE 0 END) AS books_total,
  SUM(CASE WHEN category = 'games'
      THEN amount ELSE 0 END) AS games_total
FROM orders
WHERE status = 'paid'
GROUP BY customer;
Разворот строк → колонки за одно чтение.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. В каком логическом порядке движок вычисляет эти клаузы?
    • WHERE → GROUP BY → HAVING
    • HAVING → WHERE → GROUP BY
    • GROUP BY → HAVING → WHERE
  2. Какое условие должно остаться в WHERE, а не в HAVING?
    • SUM(amount) > 100
    • status = 'paid'
    • COUNT(*) >= 2
  3. Что вычисляет SUM(CASE WHEN category = 'books' THEN amount ELSE 0 END)?
    • Сумму всех заказов
    • Сумму только тех строк, что относятся к категории 'books'
    • Количество строк в категории 'books'
    • Сумму строк вне категории 'books'