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

Un modo semplice per trasporre colonne e righe in SQL?

Esistono diversi modi per trasformare questi dati. Nel tuo post originale, hai dichiarato che PIVOT sembra troppo complesso per questo scenario, ma può essere applicato molto facilmente usando sia il UNPIVOT e PIVOT funzioni in SQL Server.

Tuttavia, se non hai accesso a queste funzioni, queste possono essere replicate utilizzando UNION ALL a UNPIVOT e quindi una funzione aggregata con un CASE dichiarazione in PIVOT :

Crea tabella:

CREATE TABLE yourTable([color] varchar(5), [Paul] int, [John] int, [Tim] int, [Eric] int);

INSERT INTO yourTable
    ([color], [Paul], [John], [Tim], [Eric])
VALUES
    ('Red', 1, 5, 1, 3),
    ('Green', 8, 4, 3, 5),
    ('Blue', 2, 2, 9, 1);

Union All, Aggregate e CASE Version:

select name,
  sum(case when color = 'Red' then value else 0 end) Red,
  sum(case when color = 'Green' then value else 0 end) Green,
  sum(case when color = 'Blue' then value else 0 end) Blue
from
(
  select color, Paul value, 'Paul' name
  from yourTable
  union all
  select color, John value, 'John' name
  from yourTable
  union all
  select color, Tim value, 'Tim' name
  from yourTable
  union all
  select color, Eric value, 'Eric' name
  from yourTable
) src
group by name

Vedi SQL Fiddle con demo

Il UNION ALL esegue il UNPIVOT dei dati trasformando le colonne Paul, John, Tim, Eric in file separate. Quindi applichi la funzione di aggregazione sum() con il case istruzione per ottenere le nuove colonne per ogni color .

Versione statica unpivot e pivot:

Sia il UNPIVOT e PIVOT le funzioni in SQL Server rendono questa trasformazione molto più semplice. Se conosci tutti i valori che desideri trasformare, puoi codificarli in una versione statica per ottenere il risultato:

select name, [Red], [Green], [Blue]
from
(
  select color, name, value
  from yourtable
  unpivot
  (
    value for name in (Paul, John, Tim, Eric)
  ) unpiv
) src
pivot
(
  sum(value)
  for color in ([Red], [Green], [Blue])
) piv

Vedi SQL Fiddle con demo

La query interna con UNPIVOT svolge la stessa funzione di UNION ALL . Prende l'elenco delle colonne e lo trasforma in righe, il PIVOT quindi esegue la trasformazione finale in colonne.

Versione pivot dinamica:

Se hai un numero sconosciuto di colonne (Paul, John, Tim, Eric nel tuo esempio) e quindi un numero sconosciuto di colori da trasformare puoi utilizzare sql dinamico per generare l'elenco in UNPIVOT e poi PIVOT :

DECLARE @colsUnpivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX)

select @colsUnpivot = stuff((select ','+quotename(C.name)
         from sys.columns as C
         where C.object_id = object_id('yourtable') and
               C.name <> 'color'
         for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT  ',' 
                      + quotename(color)
                    from yourtable t
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query 
  = 'select name, '[email protected]+'
      from
      (
        select color, name, value
        from yourtable
        unpivot
        (
          value for name in ('[email protected]+')
        ) unpiv
      ) src
      pivot
      (
        sum(value)
        for color in ('[email protected]+')
      ) piv'

exec(@query)

Vedi SQL Fiddle con demo

La versione dinamica interroga sia yourtable e poi sys.columns tabella per generare l'elenco degli elementi da UNPIVOT e PIVOT . Questo viene quindi aggiunto a una stringa di query da eseguire. Il vantaggio della versione dinamica è se hai un elenco mutevole di colors e/o names questo genererà l'elenco in fase di esecuzione.

Tutte e tre le query produrranno lo stesso risultato:

| NAME | RED | GREEN | BLUE |
-----------------------------
| Eric |   3 |     5 |    1 |
| John |   5 |     4 |    2 |
| Paul |   1 |     8 |    2 |
|  Tim |   1 |     3 |    9 |