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

Comment convertir les valeurs des lignes en colonnes avec un nombre de colonnes dynamique ?

Ma suggestion chaque fois que vous travaillez avec PIVOT est de toujours écrire la requête en premier avec les valeurs codées en dur, puis vous pouvez facilement convertir la requête en une solution dynamique.

Puisque vous allez avoir plusieurs valeurs de columnC qui seront convertis en colonnes, alors vous devez regarder en utilisant le row_number() fonction de fenêtrage pour générer une séquence unique pour chaque columnc basé sur les valeurs de columnA et columnB .

Le point de départ de votre requête sera :

select [ColumnA],
  [ColumnB],
  [ColumnC],
  'SampleTitle'+
  cast(row_number() over(partition by columna, columnb
                          order by columnc) as varchar(10)) seq
from DataSource;

Voir Démo. Cette requête va générer la liste des nouveaux noms de colonnes SampleTitle1 , etc :

| COLUMNA | COLUMNB | COLUMNC |          SEQ |
|---------|---------|---------|--------------|
|    5060 |    1006 |  100118 | SampleTitle1 |
|    5060 |    1006 |  100119 | SampleTitle2 |
|    5060 |    1006 |  100120 | SampleTitle3 |

Vous pouvez ensuite appliquer le pivot sur columnC avec les nouveaux noms de colonnes répertoriés dans seq :

select columnA, columnB, 
  SampleTitle1, SampleTitle2, SampleTitle3
from
(
   select [ColumnA],
    [ColumnB],
    [ColumnC],
    'SampleTitle'+
      cast(row_number() over(partition by columna, columnb
                              order by columnc) as varchar(10)) seq
   from DataSource
) d
pivot
(
  max(columnc)
  for seq in (SampleTitle1, SampleTitle2, SampleTitle3)
) piv;

Voir SQL Fiddle avec démo.

Une fois que vous avez la bonne logique, vous pouvez convertir les données en SQL dynamique. La clé ici est de générer la liste des nouveaux noms de colonne. J'utilise généralement FOR XML PATH pour cela similaire à :

select STUFF((SELECT distinct ',' + QUOTENAME(seq) 
                from
                (
                  select 'SampleTitle'+
                    cast(row_number() over(partition by columna, columnb
                                            order by columnc) as varchar(10)) seq
                  from DataSource
                ) d
        FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)') 
    ,1,1,'')

Voir Démo. Une fois que vous avez la liste des noms de colonnes, alors vous allez générer votre chaîne sql à exécuter, le code complet sera :

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(seq) 
                    from
                    (
                      select 'SampleTitle'+
                        cast(row_number() over(partition by columna, columnb
                                                order by columnc) as varchar(10)) seq
                      from DataSource
                    ) d
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT columnA, ColumnB,' + @cols + ' 
             from 
             (
               select [ColumnA],
                [ColumnB],
                [ColumnC],
                ''SampleTitle''+
                  cast(row_number() over(partition by columna, columnb
                                          order by columnc) as varchar(10)) seq
               from DataSource
            ) x
            pivot 
            (
                max(columnc)
                for seq in (' + @cols + ')
            ) p '

execute sp_executesql @query;

Voir SQL Fiddle avec démo. Celles-ci donnent un résultat :

| COLUMNA | COLUMNB | SAMPLETITLE1 | SAMPLETITLE2 | SAMPLETITLE3 |
|---------|---------|--------------|--------------|--------------|
|    5060 |    1006 |       100118 |       100119 |       100120 |
|    5060 |    1007 |       100121 |       100122 |       (null) |
|    5060 |    1012 |       100123 |       (null) |       (null) |