Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Query a campi incrociati con colonne dinamiche in SQL Server 2005 in poi

Esistono due modi per eseguire un PIVOT statico in cui si codificano i valori e dinamico in cui vengono determinate le colonne durante l'esecuzione.

Anche se vorrai una versione dinamica, a volte è più facile iniziare con un PIVOT statico e poi lavorare verso uno dinamico.

Versione statica:

SELECT studentid, name, sex,[C], [C++], [English], [Database], [Math], total, average
from 
(
  select s1.studentid, name, sex, subjectname, score, total, average
  from Score s1
  inner join
  (
    select studentid, sum(score) total, avg(score) average
    from score
    group by studentid
  ) s2
    on s1.studentid = s2.studentid
) x
pivot 
(
   min(score)
   for subjectname in ([C], [C++], [English], [Database], [Math])
) p

Vedi SQL Fiddle con demo

Ora, se non conosci i valori che verranno trasformati, puoi utilizzare Dynamic SQL per questo:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(SubjectName) 
                    from Score
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')



set @query = 'SELECT studentid, name, sex,' + @cols + ', total, average
              from 
             (
                select s1.studentid, name, sex, subjectname, score, total, average
                from Score s1
                inner join
                (
                  select studentid, sum(score) total, avg(score) average
                  from score
                  group by studentid
                ) s2
                  on s1.studentid = s2.studentid
            ) x
            pivot 
            (
                min(score)
                for subjectname in (' + @cols + ')
            ) p '

execute(@query)

Vedi SQL Fiddle con demo

Entrambe le versioni produrranno gli stessi risultati.

Giusto per completare la risposta, se non hai un PIVOT funzione, quindi puoi ottenere questo risultato usando CASE e una funzione aggregata:

select s1.studentid, name, sex, 
  min(case when subjectname = 'C' then score end) C,
  min(case when subjectname = 'C++' then score end) [C++],
  min(case when subjectname = 'English' then score end) English,
  min(case when subjectname = 'Database' then score end) [Database],
  min(case when subjectname = 'Math' then score end) Math,
  total, average
from Score s1
inner join
(
  select studentid, sum(score) total, avg(score) average
  from score
  group by studentid
) s2
  on s1.studentid = s2.studentid
group by s1.studentid, name, sex, total, average

Vedi SQL Fiddle con demo