TSQL Pivot sans fonction d'agrégation

139

J'ai une table comme celle-ci ...

CustomerID   DBColumnName   Data
--------------------------------------
1            FirstName      Joe
1            MiddleName     S
1            LastName       Smith
1            Date           12/12/2009
2            FirstName      Sam
2            MiddleName     S
2            LastName       Freddrick
2            Date           1/12/2009
3            FirstName      Jaime
3            MiddleName     S
3            LastName       Carol
3            Date           12/1/2009

Et je veux ça ...

Est-ce possible avec PIVOT?

CustomerID  FirstName   MiddleName          LastName        Date
----------------------------------------------------------------------
1           Joe             S               Smith           12/12/2009
2           Sam             S               Freddrick       1/12/2009
3           Jaime           S               Carol           12/1/2009
ctrlShiftBryan
la source

Réponses:

102

Vous pouvez utiliser l'agrégat MAX, cela fonctionnerait toujours. MAX d'une valeur = cette valeur.

Dans ce cas, vous pouvez également vous joindre 5 fois automatiquement sur customerid, filtrer par dbColumnName par référence de table. Cela fonctionnera peut-être mieux.

gbn
la source
1
cela ne fonctionnera pas si vous avez 2 clients avec le même prénom
Leonardo
1
Ça marchera. N'oubliez pas que DBColumnName est une métadonnée - vous filtrez littéralement par "CustomerID = 1 AND DBColumnName = 'FirstName'". Bien sûr, cela ne fonctionne pas si vous avez plusieurs lignes FirstName pour un CustomerID donné, mais si vous créez correctement vos tables CustomerID et DBColumnName font partie de votre clé primaire ...
4AM
7
Un peu de code / moquerie comme exemple aurait été génial et aurait rendu cette réponse parfaitement complète.
DavidScherer
167

Oui mais pourquoi !!??

   Select CustomerID,
     Min(Case DBColumnName When 'FirstName' Then Data End) FirstName,
     Min(Case DBColumnName When 'MiddleName' Then Data End) MiddleName,
     Min(Case DBColumnName When 'LastName' Then Data End) LastName,
     Min(Case DBColumnName When 'Date' Then Data End) Date
   From table
   Group By CustomerId
Charles Bretana
la source
2
^^ Cela a fonctionné pour moi. PIVOT n'est pas efficace pour les valeurs non numériques.
Dienekes
6
C'est une excellente alternative. J'utilisais Pivotdans ma requête, puis je suis passé à ceci et j'ai regardé le plan d'exécution pour exécuter les deux ensemble. Cette approche a coûté 8% et l'approche Pivot a pris 92%!
mafue
2
@CharlesBretana, vous êtes génial! Tu as sauvé mon âme! ) C'est la meilleure solution. Merci!
Chaki_Black
3
J'adore vraiment cette solution, elle s'assure également que les colonnes contiennent les données correctes au lieu de celles du pivot, merci!
Tenerezza
2
Ce travail très bien! Mais comment éviter -Warning: Null value is eliminated by an aggregate or other SET operation
GiddyUpHorsey
24
WITH pivot_data AS
(
SELECT customerid, -- Grouping Column
dbcolumnname, -- Spreading Column
data -- Aggregate Column
FROM pivot2 
)
SELECT customerid, [firstname], [middlename], [lastname]
FROM pivot_data
PIVOT (max(data) FOR dbcolumnname IN ([firstname],[middlename],[lastname])) AS p;
Vishwanath Dalvi
la source
3
Cela devrait être la réponse acceptée car elle montre l'utilisation correcte de la commande TSQL Pivot.
Ubercoder le
1
Il est à noter que dans cette requête, "pivot2" est le nom de la table dans laquelle résident les données d'origine. De plus, l'utilisation du CTE ici est superflue - la SELECTdéclaration sous le CTE aurait pu simplement spécifier le nom de la table d'origine.
STLDev
@STLDev En fait STLDev ce n'est pas ainsi que fonctionne le pivot. Nous ne connaissons pas toutes les colonnes du tableau "pivot2". Il pourrait, en fait, y avoir d'autres colonnes OP n'a pas spécifié qui sont dans le tableau. Ainsi, à moins que vous ne limitiez les colonnes - à l'aide d'une requête CTE ou de table dérivée - alors TOUTES les colonnes de la table sont utilisées dans le regroupement. En d'autres termes, le PIVOT renvoie quelque chose mais pas ce à quoi on s'attend. Il s'agit d'un concept abordé en profondeur pour l'examen de certification 70-761.
Zorkolot
2
Il convient de noter que PIVOT regroupe automatiquement les colonnes qui ne sont pas utilisées dans le PIVOT lui-même. Donc, dans cet exemple, [data] et [dbcolumnname] sont dans le PIVOT donc tout sera regroupé par [CustomerId]
Sal
9
SELECT
main.CustomerID,
f.Data AS FirstName,
m.Data AS MiddleName,
l.Data AS LastName,
d.Data AS Date
FROM table main
INNER JOIN table f on f.CustomerID = main.CustomerID
INNER JOIN table m on m.CustomerID = main.CustomerID
INNER JOIN table l on l.CustomerID = main.CustomerID
INNER JOIN table d on d.CustomerID = main.CustomerID
WHERE f.DBColumnName = 'FirstName' 
AND m.DBColumnName = 'MiddleName' 
AND l.DBColumnName = 'LastName' 
AND d.DBColumnName = 'Date' 

Edit: J'ai écrit ceci sans éditeur et n'ai pas exécuté le SQL. J'espère que vous avez l'idée.

shahkalpesh
la source
9

Ok, désolé pour la mauvaise question. gbn m'a mis sur la bonne voie. C'est ce que je cherchais dans une réponse.

SELECT [FirstName], [MiddleName], [LastName], [Date] 
FROM #temp 
PIVOT
(   MIN([Data]) 
    FOR [DBColumnName] IN ([FirstName], [MiddleName], [LastName], [Date]) 
)AS p

Ensuite, j'ai dû utiliser une instruction while et construire l'instruction ci-dessus en tant que varchar et utiliser dynmaic sql.

Utiliser quelque chose comme ça

SET @fullsql = @fullsql + 'SELECT ' + REPLACE(REPLACE(@fulltext,'(',''),')','')
SET @fullsql = @fullsql + 'FROM #temp '
SET @fullsql = @fullsql + 'PIVOT'
SET @fullsql = @fullsql + '('
SET @fullsql = @fullsql + ' MIN([Data])'
SET @fullsql = @fullsql + ' FOR [DBColumnName] IN '+@fulltext
SET @fullsql = @fullsql + ')'
SET @fullsql = @fullsql + 'AS p'

EXEC (@fullsql)

Avoir un pour construire @fulltext en utilisant une boucle while et sélectionner les noms de colonnes distincts hors de la table. Merci pour les réponses.

ctrlShiftBryan
la source
6

L'OP n'avait pas vraiment besoin de pivoter sans agrégation mais pour ceux d'entre vous qui viennent ici pour savoir comment voir:

requête cte paramétrée SQL

La réponse à cette question implique une situation où le pivot sans agrégation est nécessaire, donc un exemple de le faire fait partie de la solution.

Bielawski
la source
1

Essaye ça:

SELECT CUSTOMER_ID, MAX(FIRSTNAME) AS FIRSTNAME, MAX(LASTNAME) AS LASTNAME ...

FROM
(

SELECT CUSTOMER_ID, 
       CASE WHEN DBCOLUMNNAME='FirstName' then DATA ELSE NULL END AS FIRSTNAME,
       CASE WHEN DBCOLUMNNAME='LastName' then DATA ELSE NULL END AS LASTNAME,
        ... and so on ...
GROUP BY CUSTOMER_ID

) TEMP

GROUP BY CUSTOMER_ID
user3538033
la source
1

Cela devrait fonctionner:

select * from (select [CustomerID]  ,[Demographic] ,[Data]
from [dbo].[pivot]
) as Ter

pivot (max(Data) for  Demographic in (FirstName, MiddleName, LastName, [Date]))as bro
Randy Boamah
la source
1

Voici un excellent moyen de créer des champs dynamiques pour une requête pivot:

--summarize valeurs dans une table tmp

declare @STR varchar(1000)
SELECT  @STr =  COALESCE(@STr +', ', '') 
+ QUOTENAME(DateRange) 
from (select distinct DateRange, ID from ##pivot)d order by ID

--- voir les champs générés

print @STr

exec('  .... pivot code ...
pivot (avg(SalesAmt) for DateRange IN (' + @Str +')) AS P
order by Decile')
user7237698
la source