Розуміння функції 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. Але не можу зрозуміти це чітко. Було б дуже корисно, якщо хтось може пояснити це у наведеному вище випадку. (Або будь-які інші альтернативи, якщо такі є)

Відповіді:


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
у ЦІТЕНІ ви повинні визначити, з якого стовпця вам потрібно отримати значення. Ви про це питаєте?
Тарин

Щоб рішення STUFF працювало з дивними іменами стовпців (пробіли, дужки тощо), мені довелося це зробити SELECT distinct '],[', а також у кінці висловлювання1, 2, '') + ']'
Нат

@ 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умову для позначки часу).

Друге рішення - використовувати a 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>));

Це дає графічне пояснення того, як стовпці групування, розповсюдження та агрегування перетворюються з вихідної таблиці в поворотні, якщо це допомагає надалі.


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

1
чому <SELECT query that produces the data>не просто стіл?
Raheel Hasan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.