Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Maneira simples de transpor colunas e linhas em SQL?


Existem várias maneiras de transformar esses dados. Em sua postagem original, você afirmou que PIVOT parece muito complexo para este cenário, mas pode ser aplicado muito facilmente usando o UNPIVOT e PIVOT funções no SQL Server.

No entanto, se você não tiver acesso a essas funções, isso pode ser replicado usando UNION ALL para UNPIVOT e, em seguida, uma função agregada com um CASE instrução para PIVOT :

Criar tabela:
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, Agregado e Versão CASE:
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

Veja SQL Fiddle com demonstração

O UNION ALL executa o UNPIVOT dos dados transformando as colunas Paul, John, Tim, Eric em linhas separadas. Então você aplica a função agregada sum() com o case instrução para obter as novas colunas para cada color .

Unpivot e Pivot versão estática:

Tanto o UNPIVOT e PIVOT funções no SQL Server tornam essa transformação muito mais fácil. Se você conhece todos os valores que deseja transformar, pode codificá-los em uma versão estática para obter o resultado:
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

Veja SQL Fiddle com demonstração

A consulta interna com o UNPIVOT executa a mesma função que UNION ALL . Ele pega a lista de colunas e a transforma em linhas, o PIVOT em seguida, realiza a transformação final em colunas.

Versão dinâmica dinâmica:

Se você tiver um número desconhecido de colunas (Paul, John, Tim, Eric no seu exemplo) e, em seguida, um número desconhecido de cores para transformar, você pode usar o sql dinâmico para gerar a lista para UNPIVOT e então 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)

Veja SQL Fiddle com demonstração

A versão dinâmica consulta tanto yourtable e, em seguida, o sys.columns tabela para gerar a lista de itens para UNPIVOT e PIVOT . Isso é então adicionado a uma string de consulta a ser executada. A vantagem da versão dinâmica é se você tiver uma lista de colors em mudança e/ou names isso irá gerar a lista em tempo de execução.

Todas as três consultas produzirão o mesmo resultado:
| NAME | RED | GREEN | BLUE |
-----------------------------
| Eric |   3 |     5 |    1 |
| John |   5 |     4 |    2 |
| Paul |   1 |     8 |    2 |
|  Tim |   1 |     3 |    9 |