Понимание функции PIVOT в T-SQL

83

Я новичок в SQL.

У меня есть такая таблица:

ID | TeamID | UserID | ElementID | PhaseID | Effort
-----------------------------------------------------
1  |   1    |  1      |   3       |  5     |   6.74
2  |   1    |  1      |   3       |  6     |   8.25
3  |   1    |  1      |   4       |  1     |   2.23
4  |   1    |  1      |   4       |  5     |   6.8
5  |   1    |  1      |   4       |  6     |   1.5

И мне сказали получить такие данные

ElementID | PhaseID1 | PhaseID5 | PhaseID6
--------------------------------------------
    3     |   NULL   |   6.74   |   8.25
    4     |   2.23   |   6.8    |   1.5

Я понимаю, что мне нужно использовать функцию PIVOT. Но не могу понять это ясно. Было бы здорово, если бы кто-нибудь мог объяснить это в приведенном выше случае. (Или любые альтернативы, если таковые имеются)

Web-E
источник

Ответы:

109

PIVOTИспользуется для вращения данных из одного столбца в несколько колонок.

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

create table temp
(
  id int,
  teamid int,
  userid int,
  elementid int,
  phaseid int,
  effort decimal(10, 5)
)

insert into temp values (1,1,1,3,5,6.74)
insert into temp values (2,1,1,3,6,8.25)
insert into temp values (3,1,1,4,1,2.23)
insert into temp values (4,1,1,4,5,6.8)
insert into temp values (5,1,1,4,6,1.5)

select elementid
  , [1] as phaseid1
  , [5] as phaseid5
  , [6] as phaseid6
from
(
  select elementid, phaseid, effort
  from temp
) x
pivot
(
  max(effort)
  for phaseid in([1], [5], [6])
)p

Вот демонстрация SQL с рабочей версией.

Это также можно сделать с помощью динамической PIVOT, где вы динамически создаете список столбцов и выполняете PIVOT.

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.phaseid) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT elementid, ' + @cols + ' from 
            (
                select elementid, phaseid, effort
                from temp
           ) x
            pivot 
            (
                 max(effort)
                for phaseid in (' + @cols + ')
            ) p '


execute(@query)

Результаты для обоих:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5
Тарин
источник
1
Спасибо, получил его. Единственное, что мне нужно жестко закодировать PhaseIDперед QUOTENAME. правильно?
Web-E,
1
в QUOTENAME вы должны указать, из какого столбца вам нужно получить значения. Вы об этом спрашиваете?
Тарин
Чтобы решение STUFF работало со странными именами столбцов (пробелы, круглые скобки и т. Д.), Мне пришлось сделать это SELECT distinct '],[', а также в конце инструкции1, 2, '') + ']'
Nat
@ Web-E, к сожалению, да. В качестве обходного пути вы можете написать строку запроса в своем приложении или поиграть с динамическим SQL в хранимой процедуре.
MarcoM
7

Это самый простой пример поворота, пожалуйста, рассмотрите его.

SQL SERVER - Примеры таблиц PIVOT и UNPIVOT

Пример из приведенной выше ссылки для таблицы продуктов:

SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
 PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT

оказывает:

 PRODUCT FRED  KATE
 --------------------
 BEER     24    12
 MILK      3     1
 SODA   NULL     6
 VEG    NULL     5

Подобные примеры можно найти в сообщении блога « Сводные таблицы в SQL Server». Простой образец

Шейх Фарук
источник
также обратите внимание, что если вы извлекаете дополнительный числовой столбец из исходной таблицы, сводная таблица разбивает результаты на множество строк. Пример SELECT CUST, VEG, SODA FROM (SELECT rand() as x, CUST, PRODUCT, QTY FROM Product) up PIVOT ( SUM(x) FOR PRODUCT IN (VEG, SODA) ) AS pvt ORDER BY CUST GO Для того, чтобы это сработало, вы должны удалить qtyстолбец из источника
Рахил Хасан
4

Я могу добавить сюда кое-что, о чем никто не упомянул.

pivotФункция отлично работает , когда источник имеет 3 колонки: одна aggregate, одна разрастись колонн с for, и один в качестве опоры для rowраспределения. В примере продукта это QTY, CUST, PRODUCT.

Однако, если у вас больше столбцов в источнике, он разбивает результаты на несколько строк вместо одной строки на сводную таблицу на основе уникальных значений для каждого дополнительного столбца (как это Group Byбыло бы в простом запросе).

См. Этот пример, ive добавил столбец с отметкой времени в исходную таблицу:

введите описание изображения здесь

Теперь посмотрим на его влияние:

SELECT CUST, MILK

FROM Product
-- FROM (SELECT CUST, Product, QTY FROM PRODUCT) p
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

ORDER BY CUST

введите описание изображения здесь


Чтобы исправить это, вы можете использовать подзапрос в качестве источника, как все делали выше, - всего с 3 столбцами (это не всегда сработает для вашего сценария, представьте, если вам нужно поставить whereусловие для временной метки).

Второе решение - использовать group byи снова суммировать значения поворотного столбца.

SELECT 
CUST, 
sum(MILK) t_MILK

FROM Product
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

GROUP BY CUST
ORDER BY CUST

GO

введите описание изображения здесь

Рахил Хасан
источник
4

Сводная диаграмма используется для преобразования одного из столбцов в наборе данных из строк в столбцы (обычно это называется столбцом распространения ). В приведенном вами примере это означает преобразование PhaseIDстрок в набор столбцов, где есть один столбец для каждого отдельного значения, которое PhaseIDв данном случае может содержать - 1, 5 и 6.

Эти сводные значения сгруппированы по ElementIDстолбцу в приведенном вами примере.

Обычно вам также необходимо предоставить некоторую форму агрегации, которая дает вам значения, на которые ссылается пересечение значения распределения ( PhaseID) и значения группировки ( ElementID). Хотя в приведенном примере агрегирование, которое будет использоваться, неясно, но включает Effortстолбец.

После этого поворота столбцы группировки и распространения используются для поиска значения агрегирования . Или в вашем случае ElementIDи PhaseIDXищите Effort.

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

WITH PivotData AS
(
    SELECT <grouping column>
        , <spreading column>
        , <aggregation column>
    FROM <source table>
)
SELECT <grouping column>, <distinct spreading values>
FROM PivotData
    PIVOT (<aggregation function>(<aggregation column>)
        FOR <spreading column> IN <distinct spreading values>));

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

t_warsop
источник
3

Чтобы установить ошибку совместимости

используйте это перед использованием функции поворота

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  
Ишварр
источник
3
    SELECT <non-pivoted column>,
    [first pivoted column] AS <column name>,
    [second pivoted column] AS <column name>,
    ...
    [last pivoted column] AS <column name>
FROM
    (<SELECT query that produces the data>)
    AS <alias for the source query>
PIVOT
(
    <aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
    IN ( [first pivoted column], [second pivoted column],
    ... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;

USE AdventureWorks2008R2 ;
GO
SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost 
FROM Production.Product
GROUP BY DaysToManufacture;

    DaysToManufacture          AverageCost
0                          5.0885
1                          223.88
2                          359.1082
4                          949.4105

    -- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days, 
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost 
    FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;




Here is the result set.
Cost_Sorted_By_Production_Days    0         1         2           3       4       
AverageCost                       5.0885    223.88    359.1082    NULL    949.4105
user2211290
источник
1
почему <SELECT query that produces the data>не просто стол?
Рахил Хасан