Оператор PIVOT и его аналоги: сворачивание данных в таблицу

Зачем нужен PIVOT? Превращаем строки в столбцы!

Представьте, что у вас есть таблица с продажами в таком виде:

Месяц Товар Количество
Январь Яблоки 100
Январь Груши 50
Февраль Яблоки 80
Февраль Груши 70

А вам нужен вот такой отчёт:

Месяц Яблоки Груши
Январь 100 50
Февраль 80 70

Вот для этого и существует оператор PIVOT! Он "переворачивает" данные, преобразуя уникальные значения из одного столбца в новые столбцы.


Как работает PIVOT в SQL Server

Синтаксис оператора PIVOT в MS SQL Server:

SELECT [столбцы_не_для_сворачивания],
       [значение1] AS [новый_столбец1],
       [значение2] AS [новый_столбец2]
FROM исходная_таблица
PIVOT (
    агрегатная_функция(столбец_значений)
    FOR столбец_для_сворачивания IN ([значение1], [значение2], ...)
) AS псевдоним

Пример из жизни:

-- Преобразуем продажи по месяцам
SELECT Месяц, [Яблоки], [Груши]
FROM (
    SELECT Месяц, Товар, Количество 
    FROM Продажи
) AS ИсходныеДанные
PIVOT (
    SUM(Количество)
    FOR Товар IN ([Яблоки], [Груши])
) AS PivotTable

🔍 Разберём по шагам: 1. В подзапросе выбираем только нужные столбцы 2. SUM(Количество) — агрегируем значения 3. FOR Товар IN ([Яблоки], [Груши]) — превращаем уникальные значения столбца "Товар" в новые столбцы


Альтернативы PIVOT в других СУБД

Не все базы данных поддерживают PIVOT, но есть универсальные способы:

1. Условная агрегация с CASE

SELECT 
    Месяц,
    SUM(CASE WHEN Товар = 'Яблоки' THEN Количество ELSE 0 END) AS Яблоки,
    SUM(CASE WHEN Товар = 'Груши' THEN Количество ELSE 0 END) AS Груши
FROM Продажи
GROUP BY Месяц

2. Фильтрованные агрегатные функции (PostgreSQL)

SELECT
    Месяц,
    SUM(Количество) FILTER (WHERE Товар = 'Яблоки') AS Яблоки,
    SUM(Количество) FILTER (WHERE Товар = 'Груши') AS Груши
FROM Продажи
GROUP BY Месяц

3. Функция crosstab() в PostgreSQL

SELECT * FROM crosstab(
  'SELECT Месяц, Товар, SUM(Количество)
   FROM Продажи
   GROUP BY Месяц, Товар
   ORDER BY 1,2',
  'SELECT DISTINCT Товар FROM Продажи ORDER BY 1'
) AS ct (Месяц text, Яблоки int, Груши int);

Продвинутые техники PIVOT

Динамический PIVOT

Когда заранее неизвестно, какие значения нужно преобразовать в столбцы:

DECLARE @columns NVARCHAR(MAX) = '';
DECLARE @sql NVARCHAR(MAX) = '';

-- Формируем список столбцов динамически
SELECT @columns = @columns + '[' + Товар + '],'
FROM (SELECT DISTINCT Товар FROM Продажи) AS tmp;

SET @columns = LEFT(@columns, LEN(@columns) - 1);

-- Строим динамический запрос
SET @sql = '
SELECT Месяц, ' + @columns + '
FROM (
    SELECT Месяц, Товар, Количество
    FROM Продажи
) AS ИсходныеДанные
PIVOT (
    SUM(Количество)
    FOR Товар IN (' + @columns + ')
) AS PivotTable';

EXEC sp_executesql @sql;

PIVOT с несколькими агрегатными функциями

SELECT Месяц, [Яблоки_Кол], [Яблоки_Сум], [Груши_Кол], [Груши_Сум]
FROM (
    SELECT Месяц, Товар, Количество, Количество * Цена AS Сумма
    FROM Продажи
) AS ИсходныеДанные
PIVOT (
    SUM(Количество) FOR Товар IN ([Яблоки_Кол]) 
) AS PivotQuantity
PIVOT (
    SUM(Сумма) FOR Товар IN ([Яблоки_Сум], [Груши_Сум])
) AS PivotAmount;

Когда использовать PIVOT?

Идеальные сценарии:

  • Создание отчётов с перекрёстной табуляцией
  • Преобразование данных для визуализации
  • Анализ данных с группировкой по категориям

Когда лучше избегать:

  • При очень большом количестве уникальных значений (может создать сотни столбцов)
  • Когда данные часто меняются (динамический PIVOT сложен в поддержке)
  • В высоконагруженных OLTP-системах

Главные ошибки новичков

  1. Забывают агрегатную функцию
    PIVOT всегда требует агрегации (SUM, AVG, COUNT и т.д.)

  2. Не указывают все возможные значения
    Если в исходных данных появится новое значение, его нужно добавить в IN

  3. Путают порядок столбцов
    Столбцы в PIVOT выводятся в порядке, указанном в IN

  4. Игнорируют NULL-значения
    Можно использовать COALESCE или ISNULL для замены NULL на 0


Практическое задание

День Студент Часы
Понедельник Иванов 4
Понедельник Петров 3
Вторник Иванов 2
Вторник Сидоров 5

В таблицу:

День Иванов Петров Сидоров
Понедельник 4 3 NULL
Вторник 2 NULL 5
Скрыть рекламу навсегда

🧠 Учёба без воды и зубрёжки

Закрытый Boosty с наработками опытного преподавателя.

Объясняю сложное так, чтобы щелкнуло.

🚀 Забрать доступ к Boosty