TSQL Pivot sin función agregada

139

Tengo una mesa como esta ...

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

Y quiero esto ...

¿Es esto posible con 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
fuente

Respuestas:

102

Puede usar el agregado MAX, aún funcionaría. MAX de un valor = ese valor ..

En este caso, también podría unirse 5 veces en customerid, filtrar por dbColumnName por referencia de tabla. Puede funcionar mejor.

gbn
fuente
1
que en realidad no funcionará si tienes 2 clientes con el mismo nombre
Leonardo
1
Que funcionará. Recuerde que DBColumnName son metadatos: literalmente filtra por "CustomerID = 1 AND DBColumnName = 'FirstName'". Por supuesto, esto se rompe si tiene varias filas de FirstName para un CustomerID determinado, pero si está creando sus tablas correctamente, CustomerID y DBColumnName son parte de su clave principal ...
4AM
77
Algunos códigos / burlas como ejemplo habrían sido geniales y hubieran hecho esta respuesta perfectamente completa.
DavidScherer
167

si, pero por qué !!??

   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
fuente
2
^^ Esto funcionó para mí. PIVOT no es eficiente para valores no numéricos.
Dienekes
66
Esta es una gran alternativa. Estaba usando Pivoten mi consulta, luego cambié a esto y miré el plan de ejecución para ejecutar ambos juntos. ¡Este enfoque costó 8% y el enfoque Pivot tomó 92%!
mafue
2
@CharlesBretana, ¡eres genial! ¡Salvaste mi alma! ) Esa es la mejor solución. ¡Gracias!
Chaki_Black
3
Realmente me encanta esta solución, también se asegura de que las columnas contengan los datos correctos en lugar del Pivot, ¡gracias!
Tenerezza
2
Este trabajo genial! Pero ¿cómo puedo evitar -Warning: Null value is eliminated by an aggregate or other SET operation
GiddyUpHorsey
23
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
fuente
3
Esta debería ser la respuesta aceptada, ya que muestra el uso correcto del comando TSQL Pivot.
Ubercoder
1
Vale la pena señalar que en esta consulta, "pivot2" es el nombre de la tabla en la que residen los datos originales. Además, el uso del CTE aquí es superfluo: la SELECTdeclaración debajo del CTE podría haber especificado el nombre de la tabla original.
STLDev
@STLDev En realidad, STLDev no es así como funciona el pivote. No conocemos todas las columnas de la tabla "pivot2". De hecho, podría haber otras columnas que OP no especificó que están en la tabla. Entonces, a menos que restrinja las columnas, utilizando una consulta de tabla CTE o derivada, entonces TODAS las columnas de la tabla se usan en la agrupación. En otras palabras, el PIVOT devuelve algo pero no lo que esperamos. Este es un concepto cubierto en profundidad para el examen de certificación 70-761.
Zorkolot 01 de
2
Vale la pena señalar que PIVOT se agrupa automáticamente por las columnas que no se utilizan en el PIVOT. Entonces, en este ejemplo, [data] y [dbcolumnname] están en el PIVOT para que todo se agrupe por [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' 

Editar: he escrito esto sin un editor y no he ejecutado el SQL. Espero que captes la idea.

shahkalpesh
fuente
9

Ok, perdón por la pobre pregunta. Gbn me puso en el camino correcto. Esto es lo que estaba buscando en una respuesta.

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

Luego tuve que usar una instrucción while y construir la declaración anterior como un varchar y usar dynmaic sql.

Usando algo como esto

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)

Tener un @fulltext para construir usando un ciclo while y seleccionar los nombres de columna distintos de la tabla. Gracias por las respuestas

ctrlShiftBryan
fuente
6

El OP no necesitaba pivotar sin agregación, pero para aquellos de ustedes que vienen aquí para saber cómo ver:

consulta cte parametrizada sql

La respuesta a esa pregunta implica una situación en la que se necesita pivote sin agregación, por lo que un ejemplo de hacerlo es parte de la solución.

Bielawski
fuente
1

Prueba esto:

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
usuario3538033
fuente
1

Esto debería funcionar:

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
fuente
1

Aquí hay una excelente manera de crear campos dinámicos para una consulta dinámica:

- resumir valores en una tabla tmp

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

--- ver los campos generados

print @STr

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