web-dev-qa-db-fra.com

Comprendre la fonction PIVOT dans T-SQL

Je suis très nouveau pour SQL.

J'ai une table comme celle-ci:

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

Et on m'a dit d'obtenir des données comme celle-ci

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

Je comprends que je dois utiliser la fonction PIVOT. Mais je ne peux pas comprendre clairement. Ce serait très utile si quelqu'un pouvait l'expliquer dans le cas ci-dessus (ou une alternative le cas échéant).

65
Web-E

Un PIVOT permet de faire pivoter les données d'une colonne en plusieurs colonnes.

Pour votre exemple, voici un pivot STATIC qui signifie que vous codez en dur les colonnes que vous voulez faire pivoter:

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

Voici un démo SQL avec une version de travail.

Cela peut également être effectué via un PIVOT dynamique dans lequel vous créez la liste de colonnes de manière dynamique et exécutez le 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)

Les résultats pour les deux:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5
85
Taryn

Ce sont l’exemple très basique de pivot.

SQL SERVER - Exemples de tables PIVOT et UNPIVOT

Exemple de lien ci-dessus pour la table de produits:

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

rend:

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

Vous trouverez des exemples similaires dans l'article de blog Tableaux croisés dynamiques dans SQL Server. Un exemple simple

6
Shaikh Farooque

J'étais nouveau dans ce domaine et j'ai créé un article de Nice à ce sujet ... Mon problème était de comprendre comment appliquer correctement l'agrégation et voici mon article: http://jaider.net/posts/1176-pivot-in -sql-serveur-correct-agrégé-résultats /

Dans la solution @bluefeet, il est important de mentionner que elementid est la colonne clé de votre "invisible" Group By. En outre, vous pouvez remplacer elementid ou ajouter d'autres colonnes telles que userid.

enter image description here

3
Jaider
    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
3
user2211290

J'ai quelque chose à ajouter ici dont personne n'a parlé.

La fonction pivot fonctionne à merveille lorsque la source comporte 3 colonnes: une pour le aggregate, une pour se répandre sous forme de colonnes avec for, et une en tant que pivot pour row Distribution. Dans l'exemple de produit, il s'agit de QTY, CUST, PRODUCT.

Cependant, si vous avez plus de colonnes dans la source, les résultats seront divisés en plusieurs lignes au lieu d'une ligne par pivot basée sur des valeurs uniques par colonne supplémentaire (comme Group By ferait une simple requête).

Voir cet exemple, ive a ajouté une colonne timestamp à la table source:

enter image description here

Voyons maintenant son impact:

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

enter image description here


Pour résoudre ce problème, vous pouvez soit extraire une sous-requête en tant que source, comme tout le monde l’a fait ci-dessus - avec seulement 3 colonnes (cela ne fonctionnera pas toujours pour votre scénario, imaginez si vous avez besoin de mettre un where condition pour l'horodatage).

La deuxième solution consiste à utiliser un group by et faites à nouveau la somme des valeurs de la colonne pivotée.

SELECT 
CUST, 
sum(MILK) t_MILK

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

GROUP BY CUST
ORDER BY CUST

GO

enter image description here

3
Raheel Hasan

Pour définir une erreur de compatibilité

utilisez-le avant d'utiliser la fonction de pivot

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  
3
Easvarr