Requête croisée avec des colonnes dynamics dans SQL Server 2005

J'ai un problème avec requête Crosstab dans SQL Server.

Supposons que j'ai des données comme ci-dessous:

| ScoreID | StudentID | Name | Sex | SubjectName | Score | ------------------------------------------------------------------ | 1 | 1 | Student A | Male | C | 100 | | 2 | 1 | Student A | Male | C++ | 40 | | 3 | 1 | Student A | Male | English | 60 | | 4 | 1 | Student A | Male | Database | 15 | | 5 | 1 | Student A | Male | Math | 50 | | 6 | 2 | Student B | Male | C | 77 | | 7 | 2 | Student B | Male | C++ | 12 | | 8 | 2 | Student B | Male | English | 56 | | 9 | 2 | Student B | Male | Database | 34 | | 10 | 2 | Student B | Male | Math | 76 | | 11 | 3 | Student C | Female | C | 24 | | 12 | 3 | Student C | Female | C++ | 10 | | 13 | 3 | Student C | Female | English | 15 | | 14 | 3 | Student C | Female | Database | 40 | | 15 | 3 | Student C | Female | Math | 21 | | 16 | 4 | Student D | Female | C | 17 | | 17 | 4 | Student D | Female | C++ | 34 | | 18 | 4 | Student D | Female | English | 24 | | 19 | 4 | Student D | Female | Database | 56 | | 20 | 4 | Student D | Female | Math | 43 | 

Je veux faire une requête qui montre le résultat comme ci-dessous:

 | StuID| Name | Sex | C | C++ | Eng | DB | Math | Total | Average | | 1 | Student A | Male | 100| 40 | 60 | 15 | 50 | 265 | 54 | | 2 | Student B | Male | 77 | 12 | 56 | 34 | 76 | 255 | 51 | | 3 | Student C | Female | 24 | 10 | 15 | 40 | 21 | 110 | 22 | | 4 | Student D | Female | 17 | 34 | 24 | 56 | 43 | 174 | 34.8 | 

Comment puis-je requestr d'afficher la sortie comme ça?

Remarque:

Nom du sujet:

  • C
  • C ++
  • Anglais
  • Base de données
  • Math

    sera changé dépend de quel sujet étudie.

S'il vous plaît aller à http://sqlfiddle.com/#!6/2ba07/1 pour tester cette requête.

Il existe deux façons d'effectuer une statique PIVOT où vous PIVOT dur les valeurs et dynamic où les colonnes sont déterminées lorsque vous exécutez.

Même si vous voulez une version dynamic, il est parfois plus facile de commencer avec un PIVOT statique et de travailler ensuite vers un PIVOT dynamic.

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 produiront les mêmes résultats.

Juste pour compléter la réponse, si vous n'avez pas de fonction PIVOT , vous pouvez get 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

Cela nécessite la construction d'une string de requête SQL à l'exécution. Les noms de colonne, les counts et les types de données dans SQL Server sont toujours statiques (la raison la plus importante en est que l'optimiseur doit connaître le stream de données de la requête au moment de l'optimization).

Je vous recommand donc de créer une requête PIVOT à l'exécution et de l'exécuter via sp_executesql . Notez que vous devez coder en dur les valeurs de la colonne pivot. Faites attention à leur échapper correctement. Vous ne pouvez pas utiliser de parameters pour eux.

Vous pouvez également créer une telle requête par nombre de colonnes et utiliser des parameters uniquement pour les valeurs de pivot. Vous devrez atsortingbuer des noms de colonne factices comme Pivot0, Pivot1, ... Vous avez toujours besoin d'un model de requête par nombre de colonnes. Sauf si vous êtes prêt à coder en dur le nombre maximum de colonnes pivot dans la requête (disons 20). Dans ce cas, vous pouvez utiliser du SQL statique.

Vous devez utiliser SQL PIVOT dans ce cas. Plese référer le lien suivant:

Pivot sur le nombre de colonnes inconnu

Pivoter deux ou plusieurs colonnes dans SQL Server

Pivots avec des colonnes dynamics dans SQL Server