sql >> Database >  >> RDS >> Sqlserver

Kruistabelquery met dynamische kolommen in SQL Server 2005 up

Er zijn twee manieren om een ​​PIVOT uit te voeren statisch waar u de waarden hard codeert en dynamisch waar de kolommen worden bepaald wanneer u het uitvoert.

Ook al wil je een dynamische versie, soms is het makkelijker om te beginnen met een statische PIVOT en werk dan toe naar een dynamische.

Statische versie:

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

Zie SQL Fiddle met demo

Als u nu niet weet welke waarden zullen worden getransformeerd, kunt u hiervoor Dynamic SQL gebruiken:

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)

Zie SQL Fiddle met demo

Beide versies zullen dezelfde resultaten opleveren.

Om het antwoord af te ronden, als je geen PIVOT hebt functie, dan kunt u dit resultaat krijgen met CASE en een aggregatiefunctie:

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

Zie SQL Fiddle met demo



  1. Groeperen op eerste zoekopdracht in MySql

  2. Hoe verschillende waarden in SQL te tellen

  3. Probleem met pooling veerverbinding

  4. Hoe SqlCommand te gebruiken om DATABASE TE MAKEN met geparameteriseerde db-naam?