2013-07-30 5 views
-1

Pour une table comme:PIVOT toute la colonne par groupe défini

COL1 COL2 COL3 COL4 
item1 7/29/13 cat  blue 
item3 7/29/13 fish purple 
item1 7/30/13 rat  green 
item2 7/30/13 bat  grey 
item3 7/30/13 bird orange 

Comment vous PIVOTER pour obtenir des lignes de COL2, toutes les autres colonnes répétées à travers comme des blocs par des valeurs COL1?

COL2 COL1 COL3 COL4 COL1 COL3 COL4 COL1 COL3 COL4 
7/29/13 item1 cat  blue item2 NULL NULL item3 fish purple 
7/30/13 item1 rat  green item2 bat  grey item3 bird orange 
+0

(Ou mieux si l'itération se joint à elle-même)? – user2044384

+0

Dernier commentaire: le nombre d'éléments peut changer (peut-être ajouter un peu plus - mais le compte n'est pas> 10) - donc une série statique de jointures de la table sur elle-même n'est pas idéale. Merci! – user2044384

+0

Envisagez de gérer les problèmes d'affichage des données dans le code de niveau d'application/couche de présentation si vous en avez un (par exemple en passant un jeu de résultats ordonné à une boucle PHP). C'est beaucoup plus flexible. – Strawberry

Répondre

2

Afin d'obtenir ce résultat, vous devrez faire quelques petites choses:

  1. obtenir une liste distincte des valeurs de col1 et col2
  2. UNPIVOT les données dans vos colonnes col1, col3 et col4
  3. pivoter le résultat à partir de l'univoque

Pour obtenir la liste distincte des dates et des éléments (col1 et col2) ainsi que les valeurs de votre table existante, vous aurez besoin d'utiliser quelque chose de similaire à ce qui suit:

select t.col1, t.col2, 
    t2.col3, t2.col4, 
    row_number() over(partition by t.col2 
         order by t.col1) seq 
from 
(
    select distinct t.col1, c.col2 
    from yourtable t 
    cross join 
    (
    select distinct col2 
    from yourtable 
) c 
) t 
left join yourtable t2 
    on t.col1 = t2.col1 
    and t.col2 = t2.col2; 

Voir SQL Fiddle with Demo. Une fois que vous avez cette liste, vous devrez alors déplier les données. Il y a plusieurs façons dont vous pouvez faire cela, en utilisant la fonction UNPIVOT ou en utilisant CROSS APPLY:

select d.col2, 
    col = col+'_'+cast(seq as varchar(10)), 
    value 
from 
(
    select t.col1, t.col2, 
    t2.col3, t2.col4, 
    row_number() over(partition by t.col2 
         order by t.col1) seq 
    from 
    (
    select distinct t.col1, c.col2 
    from yourtable t 
    cross join 
    (
     select distinct col2 
     from yourtable 
    ) c 
) t 
    left join yourtable t2 
    on t.col1 = t2.col1 
    and t.col2 = t2.col2 
) d 
cross apply 
(
    select 'col1', col1 union all 
    select 'col3', col3 union all 
    select 'col4', col4 
) c (col, value); 

Voir SQL Fiddle with Demo. cela vous donnera des données qui ressemble à:

|      COL2 | COL | VALUE | 
------------------------------------------------- 
| July, 29 2013 00:00:00+0000 | col1_1 | item1 | 
| July, 29 2013 00:00:00+0000 | col3_1 | cat | 
| July, 29 2013 00:00:00+0000 | col4_1 | blue | 
| July, 29 2013 00:00:00+0000 | col1_2 | item2 | 
| July, 29 2013 00:00:00+0000 | col3_2 | (null) | 
| July, 29 2013 00:00:00+0000 | col4_2 | (null) | 

Enfin, vous appliquerez la fonction PIVOT aux éléments dans les colonnes col:

select col2, 
    col1_1, col3_1, col4_1, 
    col1_2, col3_2, col4_2, 
    col1_3, col3_3, col4_3 
from 
(
    select d.col2, 
    col = col+'_'+cast(seq as varchar(10)), 
    value 
    from 
    (
    select t.col1, t.col2, 
     t2.col3, t2.col4, 
     row_number() over(partition by t.col2 
          order by t.col1) seq 
    from 
    (
     select distinct t.col1, c.col2 
     from yourtable t 
     cross join 
     (
     select distinct col2 
     from yourtable 
    ) c 
    ) t 
    left join yourtable t2 
     on t.col1 = t2.col1 
     and t.col2 = t2.col2 
) d 
    cross apply 
    (
    select 'col1', col1 union all 
    select 'col3', col3 union all 
    select 'col4', col4 
) c (col, value) 
) src 
pivot 
(
    max(value) 
    for col in (col1_1, col3_1, col4_1, 
       col1_2, col3_2, col4_2, 
       col1_3, col3_3, col4_3) 
)piv; 

Voir SQL Fiddle with Demo. Si vous avez un nombre inconnu de valeurs, vous pouvez utiliser SQL dynamique pour obtenir le résultat:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col+'_'+cast(seq as varchar(10))) 
        from 
        (
         select row_number() over(partition by col2 
                order by col1) seq 
         from yourtable 
        ) t 
        cross apply 
        (
         select 'col1', 1 union all 
         select 'col3', 2 union all 
         select 'col4', 3 
        ) c (col, so) 
        group by col, seq, so 
        order by seq, so 
      FOR XML PATH(''), TYPE 
      ).value('.', 'NVARCHAR(MAX)') 
     ,1,1,'') 

set @query = 'SELECT col2, ' + @cols + ' 
      from 
      (
       select d.col2, 
        col = col+''_''+cast(seq as varchar(10)), 
        value 
       from 
       (
        select t.col1, t.col2, 
        t2.col3, t2.col4, 
        row_number() over(partition by t.col2 
             order by t.col1) seq 
        from 
        (
        select distinct t.col1, c.col2 
        from yourtable t 
        cross join 
        (
         select distinct col2 
         from yourtable 
        ) c 
       ) t 
        left join yourtable t2 
        on t.col1 = t2.col1 
        and t.col2 = t2.col2 
       ) d 
       cross apply 
       (
        select ''col1'', col1 union all 
        select ''col3'', col3 union all 
        select ''col4'', col4 
       ) c (col, value) 
      ) x 
      pivot 
      (
       max(value) 
       for col in (' + @cols + ') 
      ) p ' 

execute sp_executesql @query; 

Voir SQL Fiddle with Demo. Toutes les versions donneront un résultat:

|      COL2 | COL1_1 | COL3_1 | COL4_1 | COL1_2 | COL3_2 | COL4_2 | COL1_3 | COL3_3 | COL4_3 | 
---------------------------------------------------------------------------------------------------------------- 
| July, 29 2013 00:00:00+0000 | item1 | cat | blue | item2 | (null) | (null) | item3 | fish | purple | 
| July, 30 2013 00:00:00+0000 | item1 | rat | green | item2 | bat | grey | item3 | bird | orange | 
0

La méthode UNPIVOT + PIVOT dynamique est toujours cool, lorsque vous faites ce genre de chose pour un travail d » ensemble connu et limité de valeurs ultérieures JOIN bien trop (être paresseux sur le SELECT liste):

WITH cte AS (SELECT *,ROW_NUMBER() OVER (PARTITION BY COL2 ORDER BY COL1)'RowRank' 
      FROM #Table1) 
SELECT * 
FROM cte a 
LEFT JOIN cte b 
    ON a.COL2 = b.COL2 
    AND a.RowRank = b.RowRank - 1 
LEFT JOIN cte c 
    ON b.COL2 = c.COL2 
    AND b.RowRank = c.RowRank - 1 
WHERE a.RowRank = 1 

Ou si l'ordre des champs doit être maintenue:

WITH cte AS (SELECT a.*,b.RowRank 
      FROM #Table1 a 
      JOIN (SELECT Col1,ROW_NUMBER() OVER (ORDER BY Col1)'RowRank' 
        FROM #Table1 
        GROUP BY COL1) b 
       ON a.Col1 = b.Col1) 
SELECT * 
FROM cte a 
LEFT JOIN cte b 
    ON a.COL2 = b.COL2 
    AND a.RowRank = b.RowRank - 1 
LEFT JOIN cte c 
ON a.COL2 = c.COL2 
    AND a.RowRank = c.RowRank - 2 
WHERE a.RowRank = 1 

Mais cela se décompose sans valeur d'ancrage, c'est-à-dire que si aucun enregistrement n'avait item1 pour une date donnée, il ne serait pas inclus.

+0

Des suggestions pour conserver les valeurs dans le même ordre (http://sqlfiddle.com/#!3/471ab/25), ce qui signifie colonnes item1, colonnes item2, etc? – Taryn

+0

Ah, ouais ce serait un problème si l'ordre était important, pourrait utiliser une liste distincte de valeurs Col1, mais la méthode suivante 'JOIN' se décomposerait s'il n'y avait pas de valeur d'ancrage. –