使用t-sql进行枢轴转换

时间:2013-10-08 09:23:29

标签: sql sql-server-2008 tsql ssis business-intelligence

SSIS中有一个名为Pivot转换的任务,它将列更改为行, 但如何使用t-sql在sql server中执行相同的任务?

这是我的样本表

location product qty
-----------------------
delhi     PEPSI   100
GURGAON   CAKE    200
NOIDA     APPLE   150
delhi     cake    250

所以在使用ssis工具将枢轴转换为ON位置和产品作为枢轴键之后,o / p变为

location pepsi cake apple
delhi     100 null null
GURGAON   null 200 null 
NOIDA     null null 150 
delhi     null 250  null

2 个答案:

答案 0 :(得分:9)

使用PIVOT表格运算符,如下所示:

SELECT *
FROM tablename
PIVOT
(
  MAX(qty)
  FOR product IN([pepsi], [cake], [apple])
) as p;

请注意:

  • 我将MAX聚合函数与qty一起使用,如果您想要总和使用SUM或任何其他聚合函数。

    < / LI>
  • 您必须手动编写要旋转的列的值,如果要动态执行此操作而不是手动编写,则必须使用动态sql来执行此操作。

像这样:

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

select @cols = STUFF((SELECT distinct ',' +
                        QUOTENAME(product)
                      FROM tablename
                      FOR XML PATH(''), TYPE
                     ).value('.', 'NVARCHAR(MAX)') 
                        , 1, 1, '');

SELECT @query = 'SELECT *
FROM tablename
PIVOT
(
  MAX(qty)
  FOR product IN(' + @cols + ')) AS p;';

execute(@query);

答案 1 :(得分:8)

数据透视请求涉及三个逻辑处理阶段,每个阶段都包含相关元素:

  1. 分组阶段
  2. 传播阶段
  3. 以及具有关联聚合元素和聚合函数的聚合阶段。
  4. 因此,在您的案例中根据要求绘制这些阶段:

    1. 必须在‘Location’
    2. 上进行分组
    3. 传播必须基于‘Product’列值进行,最终列名称为:'Pepsi','Cake','Apple'。
    4. ‘Qty’值将被聚合以产生用于分组和传播元素的交叉值
    5. 将这些值放在标准的Pivot语句中:

      SELECT ...
      FROM <source_table_or_table_expression>
      PIVOT(<agg_func>(<aggregation_element>)
      FOR <spreading_element>
      IN (<list_of_target_columns>)) AS <result_table_alias>
      

      您的查询变为:

      select location ,[PEPSI], [CAKE],[APPLE]
      from table1
      pivot (sum(qty)
             for product
             in ( [PEPSI], [CAKE],[APPLE])) AS T
      

      重要的是要注意,使用PIVOT运算符时,您没有显式指定分组元素,因此无需在查询中使用GROUP BY。 PIVOT运营商计算出分组 元素隐式地作为源表(或表表达式)中未指定为扩展元素或聚合元素的所有属性。因此,您必须确保PIVOT运算符的源表除了分组,传播和聚合元素之外没有任何属性,因此在指定传播和聚合元素之后,剩下的唯一属性是您打算作为分组元素的属性。您可以通过不直接将PIVOT运算符应用于原始表来实现此目的,而是将表单表达式仅包含表示旋转元素的属性而不包含其他元素。

      select location ,[PEPSI], [CAKE],[APPLE]
      from (select location,product,qty 
            from table1 ) as SourceTable
            pivot (sum(qty)
                 for product
                 in ( [PEPSI], [CAKE],[APPLE])) AS T
      

      希望这有助于更好地理解Pivot运营商!!

      编辑:添加了Unpivot运营商概念:

      与Pivoting一样,Unpivoting也涉及3个逻辑阶段:

      1. 制作副本
      2. 提取元素
      3. 消除无关的交叉记录
      4. 将这些值放在标准的Unpivot语句中:

        SELECT ...
        FROM <source_table_or_table_expression>
        UNPIVOT(<target_col_to_hold_source_col_values>
        FOR <target_col_to_hold_source_col_names> IN(<list_of_source_columns>)) AS
        <result_table_alias>;
        

        根据您的案例中的要求映射这些阶段:

        1. <target_col_to_hold_source_col_values> =包含源列值的列的名称 即:保持列值[Pepsi], [Cake],[Apple],即100,250 ......您希望将一个列设为:Qty
        2. <target_col_to_hold_source_col_names> =包含源列名称的列的名称 即:将列名[Pepsi], [Cake],[Apple]保存为:product
        3. <list_of_source_columns> =您感兴趣的源表中列的名称 即:[Pepsi], [Cake],[Apple]
        4. 您的查询变为:

          SELECT location,product,qty
          FROM #temp
          UNPIVOT(qty
                  FOR product  
                  IN([Pepsi],[Cake],[Apple])) AS U;
          

          我在临时表#temp中添加了上面的Pivot语句的结果。

          这里要注意的重点是: 取消隐藏已透视的表格无法恢复原始表格,因为透视会因聚合而导致丢失详细信息。