2013-08-02 21 views
2

我试图提取一些可变销售中心的每个月的销售操作数量。 使用下面的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,但它是如此复杂,我不能使它的工作。任何人都有解决方案?

回答

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

+0

亲爱的Bluefeet,只需在我的查询结尾添加数据透视部分即可解决问题。我试图在数据透视句中不写月份数据,因为我看到我希望在将来使用参数获得学期或夸脱。如果您修改答案以纠正日期中的选择错误,我会将其标记为正确的答案。谢谢。 – Sergio

+0

@Sergio这显示了如何动态获取列,我不确定你的意思_如果您修改答案以纠正dates_上的选择错误。什么错误? – Taryn

+0

我只是表示您的选择不会为每个分隔的月份返回数据,而是针对一个范围。 – Sergio

0

不要这样做。为什么要将Sql Server与它无意做的事情对抗呢?

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

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

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

+0

感谢David的评论。我明白你的观点,但在开发过程中,我更愿意将数据处理留给数据库,这意味着它的目的。 我喜欢网络服务器(在这种情况下是数据的接收者)负责提供服务页面并处理请求和流量,这是足够的工作。 这使我能够从他们工作的数据中抽象出不同的页面。防爆。仪表板将具有相同的代码,与他们所处的数据无关。 – Sergio

+0

数据库旨在返回您的数据。这并不意味着要控制你的UI级别。 – David

+0

数据库不控制有关UI的任何内容。但它应该返回所需的数据。没有更多或更少。 我已经是数据库管理员和程序员超过15年了,我真的确定我在这些年来没有被这样的东西困惑。 – Sergio

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 

我一直在寻找它不是写在枢轴声明月份的数字,所以我只能返回一个季度,例。但是这使得工作成为必需。 谢谢。