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

Krydstabuleringsforespørgsel med dynamiske kolonner i SQL Server 2005 op

Der er to måder at udføre en PIVOT på statisk, hvor du hårdkoder værdierne og dynamisk, hvor kolonnerne bestemmes, når du udfører.

Selvom du vil have en dynamisk version, er det nogle gange nemmere at starte med en statisk PIVOT og derefter arbejde hen imod en dynamisk.

Statisk 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

Se SQL Fiddle with demo

Nu, hvis du ikke kender de værdier, der vil blive transformeret, kan du bruge Dynamic SQL til dette:

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)

Se SQL Fiddle with Demo

Begge versioner vil give de samme resultater.

Bare for at afrunde svaret, hvis du ikke har en PIVOT funktion, så kan du få dette resultat ved hjælp af CASE og en aggregeret funktion:

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

Se SQL Fiddle with Demo



  1. Sletning fra mange-til-mange SQL-Alchemy og Postgresql

  2. Introduktion til FORALL-erklæring i Oracle-databasen

  3. Hvordan tæller man kolonnerne i en MySQL-forespørgsel, når antallet af returnerede kolonner er dynamisk?

  4. Lagring af datetime som UTC i PHP/MySQL