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

Dynamic Unpivot e split Columns SQL Server 2012


Você só precisa dividir as colunas depois de fazer o UNPIVOT algo assim:
WITH Unpivoted 
AS
(
    SELECT region, lob, columns, value
    FROM Regions
    UNPIVOT
    (
       columns
       FOR value IN([GWP 2013] , [GWP 2014] ,
                    [LR 2013]  , [LR 2014] ,
                    [GWP 2015], [LR 2015])
    ) AS u
) 
SELECT 
  region, 
  lob,
  columns,
  CAST(CASE WHEN value LIKE 'GWP%' THEN REPLACE(value,'GWP ', '')
       WHEN value LIKE 'LR%' THEN REPLACE(value,'LR ', '')
  END AS INT) AS Year,
  CASE WHEN value LIKE 'GWP%' THEN 'GWP'
       WHEN value LIKE 'LR%' THEN 'LR'
  END AS Metrics
FROM Unpivoted;

E é claro que você deve fazer isso dinamicamente para evitar listar as colunas manualmente e fazê-lo dinamicamente:
DECLARE @cols AS NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);

select @cols = STUFF((SELECT distinct ',' +
                        QUOTENAME(column_name)
                      FROM information_schema.columns
                      WHERE table_name = 'Regions'
                        AND COLUMN_NAME <> 'Region' 
                        AND COLUMN_NAME <> 'LOB'
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '');


SELECT @query = 'WITH Unpivoted 
                AS
                (
                    SELECT region, lob, columns, value
                    FROM Regions
                    UNPIVOT
                    (
                       columns
                       FOR value IN('+ @cols + ')
                    ) AS u
                ) 
                SELECT 
                  region, 
                  lob,
                  columns,
                  CAST(CASE WHEN value LIKE ''GWP%'' THEN REPLACE(value,''GWP '', '''')
                       WHEN value LIKE ''LR%'' THEN REPLACE(value,''LR '', '''')
                  END AS INT) AS Year,
                  CASE WHEN value LIKE ''GWP%'' THEN ''GWP''
                       WHEN value LIKE ''LR%'' THEN ''LR''
                  END AS Metrics
                FROM Unpivoted';


EXECUTE(@query);

Isso deve funcionar bem supondo que:
  • Todas as colunas [GWP 2013] , [GWP 2014] , [LR 2013] , [LR 2014] , [GWP 2015], [LR 2015], ... etc estão no mesmo formato (GWP ou LR então espaço depois o ano, e

  • Todas as colunas são do mesmo tipo de dados int ou decimal, se os tipos de dados não forem os mesmos, você deve converter todos eles em um tipo de dados antes de fazer o unpivot caso contrário, você terá um erro.

  • Demonstração do SQL Fiddle

Isso lhe dará:
| region |     lob |  columns | Year | Metrics |
|--------|---------|----------|------|---------|
|  North | Workers |  38902.5 | 2013 |     GWP |
|  North | Workers | 37972404 | 2014 |     GWP |
|  North | Workers |       70 | 2015 |     GWP |
|  North | Workers |       89 | 2013 |      LR |
|  North | Workers |       82 | 2014 |      LR |
|  North | Workers |       80 | 2015 |      LR |

Atualização:


Eu usei FOR XML PATH('') .. para concatenar toda a lista de valores em uma string, é uma solução alternativa no SQL Server para fazer isso. O valor de @cols será a string:[GWP 2013], [GWP 2014], ... .

Se os tipos de dados dos seus campos forem diferentes, você deve fazer o cast de todas as colunas que serão unpivot na consulta âncora antes de fazer o UNPVOT assim:
SELECT @query = 'WITH Unpivoted 
                AS
                (
                    SELECT region, lob, columns, value
                    FROM 
                    (
                       SELECT 
                         region,
                         lob,
                         CAST([GWP 2013] AS DECIMAL(10,2)) AS [GWP 2013],
                         CAST([GWP 2014] AS DECIMAL(10,2)) AS [GWP 2014],
                         ... etc
                       FROM Regions
                    ) AS t
                    UNPIVOT
                    (
                       columns
                       FOR value IN('+ @cols + ')
                    ) AS u
                ) 
                SELECT 
                  region, 
                  lob,
                  columns,
                  CAST(CASE WHEN value LIKE ''GWP%'' THEN REPLACE(value,''GWP '', '''')
                       WHEN value LIKE ''LR%'' THEN REPLACE(value,''LR '', '''')
                  END AS INT) AS Year,
                  CASE WHEN value LIKE ''GWP%'' THEN ''GWP''
                       WHEN value LIKE ''LR%'' THEN ''LR''
                  END AS Metrics
                FROM Unpivoted';

Se você achou difícil escrever o cast para todas as colunas manualmente, você pode gerá-lo dinamicamente e anexá-lo, por exemplo:
DECLARE @colsCasted AS NVARCHAR(MAX);

select @colsCasted = STUFF((SELECT distinct ',' +
                        'CAST(' + QUOTENAME(column_name) + 'AS DECIMAL(10,2)) AS ' + QUOTENAME(column_name)
                      FROM information_schema.columns
                      WHERE table_name = 'Regions'
                        AND COLUMN_NAME <> 'Region' 
                        AND COLUMN_NAME <> 'LOB'
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '');

Em seguida, na consulta dinâmica, anexe esse valor a ela:
SELECT @query = 'WITH Unpivoted 
                    AS
                    (
                        SELECT region, lob, columns, value
                        FROM 
                        (
                          SELECT region, lob,
                          ' + @colsCasted + '
                          FROM Regions
                        ) AS t
                        UNPIVOT
                        (
                           columns
                           FOR value IN('+ @cols + ')
                        ) AS u
                    ) 
                    SELECT 
                      region, 
                      lob,
                      columns,
                      CAST(CASE WHEN value LIKE ''GWP%'' THEN REPLACE(value,''GWP '', '''')
                           WHEN value LIKE ''LR%'' THEN REPLACE(value,''LR '', '''')
                      END AS INT) AS Year,
                      CASE WHEN value LIKE ''GWP%'' THEN ''GWP''
                           WHEN value LIKE ''LR%'' THEN ''LR''
                      END AS Metrics
                    FROM Unpivoted';


    EXECUTE(@query);