Pivô do TSQL sem função agregada

139

Eu tenho uma mesa assim ...

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

E eu quero isso ...

Isso é possível usando o 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
fonte

Respostas:

102

Você pode usar o agregado MAX, ainda funcionaria. MAX de um valor = esse valor ..

Nesse caso, você também pode ingressar automaticamente 5 vezes no customerid, filtrar por dbColumnName por referência de tabela. Pode funcionar melhor.

gbn
fonte
1
que na verdade não funcionará se você tiver 2 clientes com o mesmo nome #
Leonardo Leonardo
1
Isso vai funcionar. Lembre-se de que DBColumnName são metadados - você literalmente filtra por "CustomerID = 1 AND DBColumnName = 'FirstName'". Naturalmente, esta quebra se você tem várias linhas de nome para um determinado Cliente, mas se você está criando suas tabelas adequadamente tanto CustomerID e DBColumnName são parte de sua chave primária ...
04:00
7
Alguns códigos / zombarias, por exemplo, teriam sido ótimos e tornaram essa resposta perfeitamente completa.
21418 DavidScherer
167

sim mas 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
fonte
2
^^ Isso funcionou para mim. O PIVOT não é eficiente para valores não numéricos.
Dienekes
6
Esta é uma ótima alternativa. Eu estava usando Pivotminha consulta, depois mudei para isso e observei o plano de execução para executar os dois juntos. Essa abordagem custou 8% e a abordagem Pivot levou 92%!
usar o seguinte comando
2
@CharlesBretana, você é ótimo! Você salvou minha alma! ) Essa é a melhor solução. Obrigado!
precisa saber é o seguinte
3
Realmente adoro esta solução, também garante que as colunas contenham os dados corretos em vez dos Pivot, obrigado!
tenerezza
2
Este trabalho é ótimo! Mas como posso impedir -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
fonte
3
Essa deve ser a resposta aceita, pois mostra o uso adequado do comando TSQL Pivot.
Ubercoder 02/01/19
1
Vale ressaltar que nesta consulta "pivot2" é o nome da tabela na qual os dados originais residem. Além disso, o uso do CTE aqui é supérfluo - a SELECTinstrução abaixo do CTE poderia ter especificado o nome da tabela original.
STLDev 30/03/19
@STLDev Na verdade, o STLDev não é assim que funciona o pivô. Não conhecemos todas as colunas da tabela "pivot2". De fato, pode haver outras colunas que o OP não especificou que estão na tabela. Portanto, a menos que você restrinja as colunas - usando uma consulta de tabela CTE ou Derivada -, TODAS as colunas da tabela serão usadas no agrupamento. Em outras palavras, o PIVOT retorna algo, mas não o que esperamos. Este é um conceito abordado em profundidade para o exame de certificação 70-761.
Zorkolot 01/07/19
2
Vale a pena notar que o PIVOT agrupa automaticamente o que quer que as colunas não sejam usadas no próprio PIVOT. Assim, neste exemplo [de dados] e [dbcolumnname] estão no PIVOT então tudo vai ser agrupados 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' 

Edit: Eu escrevi isso sem um editor e não execute o SQL. Espero que você tenha a idéia.

shahkalpesh
fonte
9

Ok, desculpe pela má pergunta. gbn me colocou no caminho certo. Era isso que eu estava procurando em uma resposta.

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

Então eu tive que usar uma instrução while e criar a instrução acima como um varchar e usar sql dynmaic.

Usando algo parecido com isto

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)

Para criar o @fulltext usando um loop while e selecione os nomes de coluna distintos da tabela. Obrigado pelas respostas.

ctrlShiftBryan
fonte
6

Na verdade, o OP não precisava girar sem agregação, mas para aqueles de vocês que vêm aqui para saber como ver:

consulta cte parametrizada sql

A resposta a essa pergunta envolve uma situação em que o pivô sem agregação é necessário; portanto, um exemplo disso é parte da solução.

bielawski
fonte
1

Tente o seguinte:

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

Isso deve 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
fonte
1

Aqui está uma ótima maneira de criar campos dinâmicos para uma consulta dinâmica:

- resumir valores em uma tabela tmp

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

--- veja os campos gerados

print @STr

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