Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Kreuztabellenabfrage mit dynamischen Spalten in SQL Server 2005 aufwärts

Es gibt zwei Möglichkeiten, einen PIVOT durchzuführen static, wo Sie die Werte fest codieren, und dynamic, wo die Spalten bei der Ausführung bestimmt werden.

Auch wenn Sie eine dynamische Version wünschen, ist es manchmal einfacher, mit einem statischen PIVOT zu beginnen und dann auf eine dynamische hinarbeiten.

Statische Version:

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

Siehe SQL Fiddle mit Demo

Wenn Sie die zu transformierenden Werte nicht kennen, können Sie dafür Dynamic SQL verwenden:

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)

Siehe SQL-Fiddle mit Demo

Beide Versionen liefern die gleichen Ergebnisse.

Nur um die Antwort abzurunden, falls Sie kein PIVOT haben Funktion, dann können Sie dieses Ergebnis mit CASE erhalten und eine Aggregatfunktion:

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

Siehe SQL-Fiddle mit Demo