Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Requête d'analyse croisée avec des colonnes dynamiques dans SQL Server 2005 et supérieur

Il existe deux manières d'effectuer un PIVOT statique où vous codez en dur les valeurs et dynamique où les colonnes sont déterminées lors de l'exécution.

Même si vous souhaitez une version dynamique, il est parfois plus facile de commencer avec un PIVOT statique puis travaillez vers une dynamique.

Version statique :

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

Voir SQL Fiddle avec démo

Maintenant, si vous ne connaissez pas les valeurs qui seront transformées, vous pouvez utiliser Dynamic SQL pour cela :

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)

Voir SQL Fiddle avec démo

Les deux versions donneront les mêmes résultats.

Juste pour compléter la réponse, si vous n'avez pas de PIVOT fonction, alors vous pouvez obtenir ce résultat en utilisant CASE et une fonction d'agrégat :

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

Voir SQL Fiddle avec démo