动态逆透视和拆分列 SQL Server 2012

Moh*_*hit 1 sql sql-server pivot unpivot sql-server-2012

我有一个表作为 MarketOutput 有 20 列

[Region] [LOB]  [GWP 2013]  [GWP 2014]  [LR 2013]   [LR 2014]
-------------------------------------------------------------
North  Workers  38902.50     37,972,404   89             82
Run Code Online (Sandbox Code Playgroud)

我想将列动态更改为行。Region 和 LOB 是固定列,[GWP 2013], [GWP 2014], [LR 2013], [LR 2014] 是动态列。

明年他们将是[GWP 2015][LR 2015]

我想取消旋转列并将 [GWP 2014] 拆分为两列 [GWP], [2014]。

输出应该是这样的

Region  [LOB]      [Metrics] [Year] [Value]
--------------------------------------------------
North   Workers    GWP       2013         38902.50 
North   Workers    GWP       2014    37,972,404
North   Workers    LR        2013            89
North   Workers    LR        2014            82
Run Code Online (Sandbox Code Playgroud)

你能建议如何做到吗?

我是在 SQL Server 中进行透视的新手

我还想每次使用动态列表将输出插入到新表中

Mah*_*mal 5

您只需要在执行以下操作后拆分列UNPIVOT

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;
Run Code Online (Sandbox Code Playgroud)

然后当然你应该动态地做,以避免手动列出列并动态地做:

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);
Run Code Online (Sandbox Code Playgroud)

假设以下情况,这应该可以正常工作:

  • 所有的列[GWP 2013] , [GWP 2014] , [LR 2013] , [LR 2014] , [GWP 2015], [LR 2015], ... etc都采用相同的格式(GWP 或 LR,然后是空格,然后是年份,以及
  • 所有的列都是相同的数据类型int或十进制,如果数据类型不同,你应该在做之前unpivot将它们全部转换为一种数据类型,否则你会得到一个错误。

  • SQL 小提琴演示

这会给你:

| 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 |
Run Code Online (Sandbox Code Playgroud)

更新:

我曾经FOR XML PATH('') ..将所有值列表连接到一个字符串中,这是 SQL Server 中的一种解决方法。的值@cols将是字符串:[GWP 2013], [GWP 2014], ...

如果您的字段数据类型不同,您必须在执行以下操作之前对将在锚查询中取消透视的所有列进行强制转换UNPVOT

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';
Run Code Online (Sandbox Code Playgroud)

如果您发现手动为所有列编写演员表很困难,您可以动态生成并附加它,例如:

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, '');
Run Code Online (Sandbox Code Playgroud)

然后在动态查询中将该值附加到它:

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);
Run Code Online (Sandbox Code Playgroud)

  • 谢谢马哈茂德的澄清。真的很感谢努力 (2认同)