转置动态数据

时间:2013-08-02 11:02:05

标签: sql-server pivot transpose

我正在尝试为许多可变销售中心提取每个月的销售业务数量。 使用以下TSQL ...

;WITH Months(m) AS
(
    SELECT 1 m
    UNION ALL 
    SELECT m+1 FROM Months WHERE m < 12
)
SELECT  t.Center,m Month, t.Sales FROM Months
CROSS APPLY 
(
    SELECT C.Center, COUNT(1) Sales FROM Operations C
    LEFT JOIN Centers A ON A.Code=C.Center
    WHERE Date BETWEEN '01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013' AND DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,'01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013')+1,0))
    GROUP BY C.Center
) t

所以我得到以下输出:

Center   Month   Sales
-----------------------
A        1       20
B        1       30
A        2       25
B        2       30
....

我想要结束的是:

Center   1    2    ...
----------------------
A        20   25   ...
B        30   30   ...

我正在研究使用xmlpath的数据透视图,但它太复杂了,我无法使其工作。有人有解决方案吗?

3 个答案:

答案 0 :(得分:1)

也许我错过了你的问题,但由于一年只有12个月,所以没有理由使用动态SQL,因为你只有12列。

使用以下查询可以轻松完成此操作:

select center, 
  [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]
from
(
  select o.center, month(date) month
  from operations o
  inner join centers c
    on o.center = c.code
  where c.date >= '2013-01-01'
    and c.date <= '2013-12-31'
) d
pivot
(
  count(month)
  for month in ([1], [2], [3], [4], [5], [6], [7], [8], [9], 
                [10], [11], [12])
) piv

请参阅SQL Fiddle with Demo

如果您想动态执行此操作,则可以使用以下内容:

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @startdate datetime,
    @enddate datetime

set @startdate = '2013-01-01'
set @enddate = '2013-12-31'

;WITH Months(m) AS
(
    SELECT 1 m
    UNION ALL 
    SELECT m+1 FROM Months WHERE m < 12
)
select @cols = STUFF((SELECT ',' + QUOTENAME(m) 
                    from Months
                    group by m
                    order by m
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query = 'SELECT center, ' + @cols + ' 
            from 
            (
              select o.center, month(date) month
              from operations o
              inner join centers c
                on o.center = c.code
              where c.date >= '''+convert(varchar(10), @startdate, 120)+'''
                and c.date <= '''+convert(varchar(10), @enddate, 120)+'''
            ) x
            pivot 
            (
                count(month)
                for month in (' + @cols + ')
            ) p '


execute sp_executesql @query;

请参阅SQL Fiddle with Demo

答案 1 :(得分:0)

不要这样做。为什么要将Sql Server打成不应该做的事情?

让您的查询垂直返回数据,就像您现在一样。

在应用程序级别转置它 编写,调试,维护和处理毫无疑问会出现的特殊业务规则会容易得多。

此外,它还可以更轻松地将数据粘贴到Excel中以进行故障排除和旋转。

答案 2 :(得分:0)

我终于实现了BlueFeet的第一个建议,稍作修改:

;WITH Months(m) AS
(
    SELECT 1 m
    UNION ALL 
    SELECT m+1 FROM Months WHERE m < 12
)
SELECT * FROM Months
CROSS APPLY 
(
    SELECT C.Center, COUNT(1) Sales FROM Operations C
    LEFT JOIN Centers A ON A.Code=C.Center
    WHERE Date BETWEEN '01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013' AND DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,'01/'+ CONVERT(VARCHAR(2),Months.m) + '/2013')+1,0))
    GROUP BY C.Center
) t
pivot
(
    max(Sales)
    for m in ([1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12])
) as PIV

我在寻找它不是在枢轴声明上写月份数字,所以我只能返回四分之一,例如。但这使得工作成为必需。 感谢。