【问题标题】:Displaying Columns as Rows in SQL Server 2005在 SQL Server 2005 中将列显示为行
【发布时间】:2013-03-23 08:45:41
【问题描述】:

我已经阅读了数十种与我即将提出的类似换位问题的解决方案,但奇怪的是没有一个完全反映我的问题。我只是想在一个简单的仪表板类型数据集中将我的行翻转为列。

从各种事务表中提取的数据如下所示:

DatePeriod  PeriodNumberOverall   Transactions   Customers   Visits

'Jan 2012'   1                    100            50          150
'Feb 2012'   2                    200            100         300
'Mar 2012'   3                    300            200         600

我希望能够生成以下内容:

                      Jan 2012   Feb 2012   Mar 2012

Transactions          100        200        300
Customers             50         100        200
Visits                150        300        600

指标将是静态的(交易、客户和访问),但日期周期将是动态的(IE - 随着月份的推移会增加更多)。

再一次,我已经准备好许多利用 pivot、unpivot、存储过程、UNION ALL 等的示例,但我没有做任何聚合,只是从字面上转置整个输出。我还在 Visual Studio 2005 中找到了一种使用带有嵌入式列表的矩阵的简单方法,但我无法将最终输出导出到 excel 中,这是必需的。任何帮助将不胜感激。

【问题讨论】:

标签: sql tsql sql-server-2005 pivot


【解决方案1】:

您需要动态地使用 PIVOTAPPLY 运算符动态创建 SQL 语句,然后运行该命令。如果您的指标是静态的(交易、客户和访问),因此我们可以使用 CROSS APPLY 运算符和 VALUES 作为表源。

对于 SQL Server2008+

DECLARE @cols nvarchar( max),
        @query nvarchar(max)
SELECT @cols =
  STUFF((SELECT ',' + QUOTENAME(t.DatePeriod) AS ColName                               
         FROM dbo.test62 t                   
         FOR XML PATH(''), TYPE).value ('.', 'nvarchar(max)'), 1, 1, '')     

SET @query =
 'SELECT *
  FROM (
        SELECT t.DatePeriod, COALESCE(o.Transactions, o.Customers, o.Visits) AS PvtVals, o.PvtColumns, o.OrderColumns 
        FROM dbo.test62 t CROSS APPLY (
                                       VALUES(t.Transactions, NULL, NULL, ''Transaction'', 1),
                                             (NULL, t.Customers, NULL, ''Customers'', 2),
                                             (NULL, NULL, t.Visits, ''Visits'', 3)
                                       ) o (Transactions, Customers, Visits, PvtColumns, OrderColumns)
        ) p
  PIVOT
   (      
    MAX(PvtVals) FOR DatePeriod IN (' + @cols + ')
    ) AS pvt
  ORDER BY pvt.OrderColumns '
EXEC(@query) 

结果:

PvtColumns  Jan 2012 Fed 2012 Mar 2012
Transaction 100      200      300
Customers   50       100      200
Visits      150      300      600

SQLFiddle上的演示

对于 SQL Server 2005

DECLARE @cols nvarchar( max),
        @query nvarchar(max)
SELECT @cols =
  STUFF((SELECT ',' + QUOTENAME(t.DatePeriod) AS ColName                               
         FROM dbo.test62 t                   
         FOR XML PATH(''), TYPE).value ('.', 'nvarchar(max)'), 1, 1, '')     

SET @query =
 'SELECT *
  FROM (
        SELECT t.DatePeriod, COALESCE(o.Transactions, o.Customers, o.Visits) AS PvtVals, o.PvtColumns, o.OrderColumns
        FROM dbo.test62 t CROSS APPLY (
                                       SELECT t.Transactions, NULL, NULL, ''Transaction'', 1
                                       UNION ALL 
                                       SELECT NULL, t.Customers, NULL, ''Customers'', 2
                                       UNION ALL 
                                       SELECT NULL, NULL, t.Visits, ''Visits'', 3
                                       ) o (Transactions, Customers, Visits, PvtColumns, OrderColumns)
        ) p
  PIVOT
   (      
    MAX(PvtVals) FOR DatePeriod IN (' + @cols + ')
    ) AS pvt
  ORDER BY pvt.OrderColumns'
EXEC(@query) 

【讨论】:

  • CROSS APPLY ... VALUES 在 2005 年不起作用。不过你可以使用 CROSS APPLY (SELECT t.Transactions, NULL, NULL, ''Transaction'' UNION ALL SELECT NULL, t.Customers, NULL, ''Customers'' UNION ALL ... )
  • 感谢 Martin 的澄清!;)答案已更新。顺便说一句,如果使用 CROSS APPLY (SELECT Transactions, Customers, Visits, PvtColumns FROM (VALUES (t.Transactions, NULL, NULL, ''Transaction''), (NULL, t.Customers, NULL, ''Customers''), (NULL, NULL, t.Visits, ''Visits'')) x(Transactions, Customers, Visits, PvtColumns)) o ... 它会起作用吗?
  • 非常感谢您的精彩回答!我剩下的唯一问题是有什么简单的方法可以按我想要的方式排序指标吗?在所有三个答案中,指标都按 alpha 顺序重新排序。再次感谢!
【解决方案2】:

为了得到你想要的结果,你需要先UNPIVOT数据,然后PIVOT theDatePeriod`值。

UNPIVOT 会将TransactionsCustomersVisits 的多列转换为多行。其他答案是使用 UNION ALL 来取消透视,但 SQL Server 2005 是支持 UNPIVOT 函数的第一年。

取消透视数据的查询是:

select dateperiod,
  col, value
from transactions
unpivot
(
  value for col in (Transactions, Customers, Visits)
) u

Demo。这会将您当前的列转换为多行,因此数据如下所示:

| DATEPERIOD |          COL | VALUE |
-------------------------------------
|   Jan 2012 | Transactions |   100 |
|   Jan 2012 |    Customers |    50 |
|   Jan 2012 |       Visits |   150 |
|   Feb 2012 | Transactions |   200 |

现在,由于数据在行中,您可以将PIVOT 函数应用于DatePeriod 列:

select col, [Jan 2012], [Feb 2012], [Mar 2012]
from
(
  select dateperiod,
    t.col, value, c.SortOrder
  from
  (
    select dateperiod,
      col, value
    from transactions
    unpivot
    (
      value for col in (Transactions, Customers, Visits)
    ) u
  ) t
  inner join
  (
    select 'Transactions' col, 1 SortOrder
    union all
    select 'Customers' col, 2 SortOrder
    union all
    select 'Visits' col, 3 SortOrder
   ) c
    on t.col = c.col
) d
pivot
(
  sum(value)
  for dateperiod in ([Jan 2012], [Feb 2012], [Mar 2012])
) piv
order by SortOrder;

SQL Fiddle with Demo

如果您有未知数量的日期周期,那么您将使用动态 SQL:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(dateperiod) 
                    from transactions
                    group by dateperiod, PeriodNumberOverall
                    order by PeriodNumberOverall
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT col, ' + @cols + ' 
             from 
             (
                select dateperiod,
                  t.col, value, c.SortOrder
                from
                (
                  select dateperiod,
                    col, value
                  from transactions
                  unpivot
                  (
                    value for col in (Transactions, Customers, Visits)
                  ) u
                ) t
                inner join
                (
                  select ''Transactions'' col, 1 SortOrder
                  union all
                  select ''Customers'' col, 2 SortOrder
                  union all
                  select ''Visits'' col, 3 SortOrder
                 ) c
                  on t.col = c.col
            ) x
            pivot 
            (
                sum(value)
                for dateperiod in (' + @cols + ')
            ) p 
            order by SortOrder'

execute(@query)

SQL Fiddle with Demo。两者都会给出结果:

|          COL | JAN 2012 | FEB 2012 | MAR 2012 |
-------------------------------------------------
| Transactions |      100 |      200 |      300 |
|    Customers |       50 |      100 |      200 |
|       Visits |      150 |      300 |      600 |

【讨论】:

  • 非常感谢您的精彩回答!我剩下的唯一问题是有什么简单的方法可以按我想要的方式排序指标吗?在所有三个答案中,指标都按 alpha 顺序重新排序。再次感谢!
  • @user2234794 查看我的编辑,我更新了我的答案以包括对我的列初始位置进行排序
  • 感谢您的更新。我将用于为我的仪表板转置的主要“表”将不是架构的一部分——它将是从多个其他表中提取的数据元素(聚合指标/字段)到一个临时表中。如果我没有从架构中存在的单个表中提取字段,是否还有另一种方法来进行排序?抱歉,如果问题很简单/基本 - 无论如何我都不是 T-SQL 专家。
  • @user2234794 是的,您可以加入包含排序顺序的派生表。查看我的编辑
【解决方案3】:

如果你能提前知道有多少个不同的日期周期,那么你可以使用固定查询,如下所示:


;with CTE_UNIONTable
as 
(
select [DatePeriod],[PeriodNumberOverall],[Transactions] as [value], 'Transactions' as subType from table1
UNION ALL 
select [DatePeriod],[PeriodNumberOverall],[Customers] as [value], 'Customers' as subType from table1
UNION ALL 
select [DatePeriod],[PeriodNumberOverall],[Visits] as [value], 'Visits' as subType from table1
), CTE_MiddleResult
as 
(
select * from CTE_UNIONTable
    pivot 
    (
       max(value) 
       for DatePeriod in ([Jan 2012],[Feb 2012],[Mar 2012])
     ) as P
     )
select SubType, max([Jan 2012]) as [Jan 2012] ,max([Feb 2012]) as [Feb 2012], max([Mar 2012]) as [Feb 2012]
from CTE_MiddleResult
group by SubType

SQL FIDDLE DEMO

如果有多少个日期周期是不可预知的,那么@Alexander已经给出了解决方案,下面的代码只是第二个意见,而不是使用APPLY,使用UNION ALL


DECLARE @cols nvarchar( max),
        @query nvarchar (max),
        @selective nvarchar(max)
SELECT @cols =
  STUFF((SELECT ',' + QUOTENAME(t.DatePeriod) AS ColName                               
         FROM table1 t                   
         FOR XML PATH( ''), TYPE).value ('.', 'nvarchar(max)'),1,1,'')

SELECT @selective =
  STUFF((SELECT ',MAX(' + QUOTENAME(t.DatePeriod) +') as ' + QUOTENAME(t.DatePeriod)  AS ColName                               
         FROM table1 t                   
         FOR XML PATH( ''), TYPE).value ('.', 'nvarchar(max)'),1,1,'')

set @query = '
;with CTE_UNIONTable
as 
(
select [DatePeriod],[PeriodNumberOverall],[Transactions] as [value], ''Transactions'' as subType from table1
UNION ALL 
select [DatePeriod],[PeriodNumberOverall],[Customers] as [value], ''Customers'' as subType from table1
UNION ALL 
select [DatePeriod],[PeriodNumberOverall],[Visits] as [value], ''Visits'' as subType from table1
), CTE_MiddleResult
as 
(
select * from CTE_UNIONTable
    pivot 
    (
       max(value) 
       for DatePeriod in ('+@cols+')
     ) as P
)
select SubType,' + @selective + ' 
from CTE_MiddleResult
group by SubType'

exec(@query)

SQL FIDDLE DEMO

【讨论】:

  • 非常感谢您的精彩回答!我剩下的唯一问题是有什么简单的方法可以按我想要的方式排序指标吗?在所有三个答案中,指标都按 alpha 顺序重新排序。再次感谢!
  • 可以在最后加上ORDER BY,但是不管是asc还是desc,你想要什么确切的顺序,可能代码需要根据你想要的顺序稍微更新一下。
猜你喜欢
  • 2017-10-07
  • 1970-01-01
  • 2012-08-04
  • 1970-01-01
  • 2012-03-19
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多