Zrozumienie funkcji PIVOT w T-SQL

83

Jestem bardzo nowy w SQL.

Mam taki stół:

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

Powiedziano mi, żebym uzyskał takie dane

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

Rozumiem, że muszę użyć funkcji PIVOT. Ale nie mogę tego jasno zrozumieć. Byłoby bardzo pomocne, gdyby ktoś mógł to wyjaśnić w powyższym przypadku. (Lub jakiekolwiek alternatywy, jeśli istnieją)

Web-E
źródło

Odpowiedzi:

109

PIVOTSłużą do obracania danych z jednej kolumny w wielu kolumnach.

Na przykład tutaj jest STATYCZNY Pivot, co oznacza, że ​​na stałe kodujesz kolumny, które chcesz obrócić:

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

Oto demo SQL z działającą wersją.

Można to również zrobić za pomocą dynamicznego PRZESUWU, w którym tworzysz listę kolumn dynamicznie i wykonujesz PRZELOT.

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)

Wyniki dla obu:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5
Taryn
źródło
1
Dzięki, rozumiem. Jedyne, czego potrzebuję, to twardy kod PhaseIDprzed QUOTENAME. dobrze?
Web-E
1
w QUOTENAME musisz określić, z której kolumny chcesz pobrać wartości. Czy o to pytasz?
Taryn
Aby rozwiązanie STUFF działało z dziwnymi nazwami kolumn (spacje, nawiasy itp.) Musiałem to zrobić SELECT distinct '],[', a także na końcu instrukcji1, 2, '') + ']'
Nat
@ Web-E, niestety tak. Aby obejść ten problem, możesz napisać ciąg zapytania w aplikacji lub pobawić się dynamicznym SQL w procedurze składowanej.
MarcoM
7

To są bardzo podstawowe przykłady pivotów, proszę przejść przez to.

SERWER SQL - Przykłady tabel przestawnych i UNPIVOT

Przykład z powyższego linku do tabeli produktów:

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

renderuje:

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

Podobne przykłady można znaleźć w poście na blogu Tabele przestawne w programie SQL Server. Prosta próbka

Shaikh Farooque
źródło
Zwróć również uwagę, że jeśli wyciągniesz dodatkową kolumnę liczbową z tabeli źródłowej, przestawienie podzieli wyniki na wiele wierszy. Przykład 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 Aby to zadziałało, musisz usunąć qtykolumnę ze źródła
Raheel Hasan
4

Mam tu coś do dodania, o czym nikt nie wspomniał.

pivotFunkcja działa świetnie, kiedy źródło ma 3 kolumny: jeden dla aggregate, jeden rozprzestrzeniać jak z kolumnami fori jeden za przegub rowdystrybucji. W przykładzie produktu to jest QTY, CUST, PRODUCT.

Jeśli jednak masz więcej kolumn w źródle, wyniki zostaną podzielone na wiele wierszy zamiast jednego wiersza na oś na podstawie unikatowych wartości na dodatkową kolumnę (tak Group Byjak w przypadku prostego zapytania).

Zobacz ten przykład, ive dodał kolumnę timestamp do tabeli źródłowej:

wprowadź opis obrazu tutaj

Teraz zobacz jego wpływ:

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

wprowadź opis obrazu tutaj


Aby to naprawić, możesz albo pobrać podzapytanie jako źródło, tak jak wszyscy zrobili to powyżej - z tylko 3 kolumnami (nie zawsze to zadziała w Twoim scenariuszu, wyobraź sobie, że musisz umieścić wherewarunek dla znacznika czasu).

Drugim rozwiązaniem jest użycie a group byi ponowne wykonanie sumy wartości kolumn przestawnych.

SELECT 
CUST, 
sum(MILK) t_MILK

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

GROUP BY CUST
ORDER BY CUST

GO

wprowadź opis obrazu tutaj

Raheel Hasan
źródło
4

Pivot służy do konwersji jednej z kolumn w zestawie danych z wierszy na kolumny (jest to zwykle nazywane kolumną rozkładającą ). W podanym przykładzie oznacza to przekształcenie PhaseIDwierszy w zestaw kolumn, w którym dla każdej odrębnej wartości PhaseIDmoże znajdować się jedna kolumna, która w tym przypadku może zawierać - 1, 5 i 6.

Te przestawne wartości są pogrupowane za pośrednictwem ElementIDkolumny w podanym przykładzie.

Zwykle trzeba wtedy również podać jakąś formę agregacji, która daje wartości, do których odwołuje się przecięcie wartości rozłożenia ( PhaseID) i wartości grupowania ( ElementID). Chociaż w podanym przykładzie agregacja, która zostanie użyta, jest niejasna, ale obejmuje Effortkolumnę.

Po zakończeniu tego obracania kolumny grupujące i rozkładające są używane do znalezienia wartości agregacji . Lub w twoim przypadku ElementIDi PhaseIDXwyszukaj Effort.

Używając terminologii grupowania, rozpraszania i agregacji , zazwyczaj zobaczysz przykładową składnię dla przestawienia jako:

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>));

To daje graficzny wyjaśnienie, w jaki sposób grupowania, rozkładanie i agregacji kolumny konwersji od źródła do stołów obrotowych, jeśli pomaga dalej.

t_warsop
źródło
3

Aby ustawić błąd zgodności

użyj tego przed użyciem funkcji pivot

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  
Easvarr
źródło
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
źródło
1
dlaczego nie jest to <SELECT query that produces the data>zwykły stół?
Raheel Hasan