Поворот строк в столбцы SQL: выручка по каналам

Руководитель просит месячный отчёт: один ряд на месяц, рядом выручка из прямого трафика, рекламы и партнёров. Исходная таблица устроена наоборот: каждое поступление хранит месяц, канал и сумму. Для фиксированного набора каналов достаточно условной агрегации — сумма попадает в свой столбец, когда выполняется условие.

Здесь важно не потерять доход, чей канал пока не попал в известные три значения. Если просто сделать три SUM(CASE ...), июньская сумма 5 с меткой unknown исчезнет из видимых столбцов, хотя общий итог останется 205. Поэтому добавляем other и общий total, чтобы было видно расхождение и его источник.

Один проход по данным, несколько столбцов

CASE возвращает сумму строки только для нужного канала, иначе ноль. Группировка по месяцу складывает эти значения. Это удобно, пока список столбцов отчёта заранее известен. На маленьком наборе можно сразу проверить равенство: сумма direct + ads + partner + other должна совпасть с total.

CREATE TABLE orders (month_start TEXT, channel TEXT NOT NULL, revenue INTEGER);
INSERT INTO orders VALUES
 ('2025-06-01','direct',100),('2025-06-01','ads',80),
 ('2025-06-01','partner',20),('2025-06-01','unknown',5),
 ('2025-07-01','direct',40),('2025-07-01','ads',60);
SELECT month_start,
 SUM(CASE WHEN channel='direct' THEN revenue ELSE 0 END) AS direct,
 SUM(CASE WHEN channel='ads' THEN revenue ELSE 0 END) AS ads,
 SUM(CASE WHEN channel='partner' THEN revenue ELSE 0 END) AS partner,
 SUM(CASE WHEN channel NOT IN ('direct','ads','partner') THEN revenue ELSE 0 END) AS other,
 SUM(revenue) AS total
FROM orders GROUP BY month_start ORDER BY month_start;

Июньская строка даст 100, 80, 20, 5 и общий итог 205. Июльская — 40, 60, 0, 0 и итог 100. Ноль у партнёров в июле означает, что входная таблица содержит июль, но не содержит продаж этого канала. Если самого месяца нет во входе, строка месяца вовсе не появится; для календарного отчёта тогда нужен отдельный календарь.

Когда число каналов растёт

Если менеджер каждый день добавляет новые каналы и хочет отдельный столбец для каждого, статический SQL придётся менять. Часто лучше отдавать длинную таблицу «месяц — канал — выручка», а поворот делать в инструменте визуализации. Статический pivot полезен, когда набор бизнес-категорий устойчив и зафиксирован в отчётном контракте.

Перед поворотом договоритесь о зерне и атрибуции. Если одна покупка получила два канала после соединения с рекламными касаниями, её сумма попадёт в два столбца и общий итог удвоится. Сначала присвойте заказу один согласованный канал либо явно распределите сумму между несколькими. Условная агрегация не исправит неправильный вход.

В коде channel объявлен NOT NULL. Если реальный источник допускает NULL, строка не попадёт ни в один из известных каналов и условие NOT IN тоже её не поймает. Добавьте в ветку other явное OR channel IS NULL. Затем сравните сумму четырёх категорий с total: она должна совпасть для каждого месяца.

Переименование канала тоже влияет на отчёт. Если в июле значение ads переименовали в paid, его выручка уйдёт в other и рекламный столбец будто обнулится. Для устойчивого отчёта нормализуйте технические значения к небольшому справочнику бизнес-каналов и версионируйте правила соответствия. Пик в other тогда станет полезным сигналом о новом значении в источнике.

Когда отчёт строится по нескольким валютам, нельзя складывать их в один столбец выручки до согласованной конвертации. Условная агрегация повернёт таблицу без ошибок SQL, но числа будут экономически бессмысленны. Сначала определите валюту отчёта и дату курса для операций, затем считайте месячные суммы по каналам.

В реальном дашборде полезно проверять не только общий итог месяца, но и долю other. Если она выросла с почти нуля до заметной части выручки, технический справочник каналов, вероятно, изменился. Такой сигнал можно вывести рядом с графиком, пока новое значение не будет классифицировано. Молча прибавлять его к одному из известных каналов означает переписать историю атрибуции.

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

В «Продвинутом SQL» сложные соединения, предварительная агрегация и сверка отчётов приводят к витрине интернет-магазина. Студент проверяет запросы на SQLite и видит, когда сумма размножается после JOIN. Откройте программу, чтобы строить широкие отчёты из проверенного источника данных.

Для отдельной проблемы неизвестного канала прочитайте почему unknown нельзя записывать в органику.

Продолжить на практике