2011-12-01 98 views
38

我有一个具有此架构SQL Server组按月

ItemID UserID Year IsPaid PaymentDate Amount 
1   1   2009 0   2009-11-01 300 
2   1   2009 0   2009-12-01 342 
3   1   2010 0   2010-01-01 243 
4   1   2010 0   2010-02-01 2543 
5   1   2010 0   2010-03-01 475 

我试图让查询工作,显示每月总计的表。到目前为止,我已经尝试了DateDiff和嵌套选择,但都没有给我我想要的。这是我最接近的我的想法:

DECLARE @start [datetime] = 2010/4/1; 
SELECT ItemID, IsPaid, 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 And DateDiff(m, PaymentDate, @start) = 0 AND UserID = 100) AS "Apr", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =1 AND UserID = 100) AS "May", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =2 AND UserID = 100) AS "Jun", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =3 AND UserID = 100) AS "Jul", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =4 AND UserID = 100) AS "Aug", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =5 AND UserID = 100) AS "Sep", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =6 AND UserID = 100) AS "Oct", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =7 AND UserID = 100) AS "Nov", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =8 AND UserID = 100) AS "Dec", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =9 AND UserID = 100) AS "Jan", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =10 AND UserID = 100) AS "Feb", 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 AND DateDiff(m, PaymentDate, @start) =11 AND UserID = 100) AS "Mar" 
FROM LIVE L INNER JOIN Payments I ON I.LiveID = L.RECORD_KEY 
WHERE UserID = 16178 

但我只是得到空值,当我应该得到的价值。我错过了什么吗?

+0

您是否试图将表转换为按年份/月显示列,并在其下使用userid进行付款? – William

+0

为什么您的UserID = 16178中的where子句与子查询where子句中的UserID = 100不同?另外,1月,2月和3月的最后3个子查询分别是从4月份的9月份,10月份和11月份的月份差异? –

+0

可能的重复[如何从日期字段使用sql按月分组](http://stackoverflow.com/questions/14565788/how-to-group-by-mon-month-from-date-field-using-sql) – kurast

回答

84
SELECT CONVERT(NVARCHAR(10), PaymentDate, 120) [Month], SUM(Amount) [TotalAmount] 
FROM Payments 
GROUP BY CONVERT(NVARCHAR(10), PaymentDate, 120) 
ORDER BY [Month] 

您也可以尝试:

SELECT DATEPART(Year, PaymentDate) Year, DATEPART(Month, PaymentDate) Month, SUM(Amount) [TotalAmount] 
FROM Payments 
GROUP BY DATEPART(Year, PaymentDate), DATEPART(Month, PaymentDate) 
ORDER BY Year, Month 
+5

DatePart是我需要的。谢谢。 – Echilon

+0

只需在MySql中使用逗号,就像这个'按年份(日期),月份(日期)组'一样' – simo

+0

为了可读性而选择第二个答案。 –

3
DECLARE @start [datetime] = 2010/4/1; 

应该是...

DECLARE @start [datetime] = '2010-04-01'; 

你拥有的是一个2010除以4,然后按1,然后转换到一个约会。这是1900-01-01第57.5天。

尝试SELECT @start初始化后检查这是否正确。

3

如果你需要频繁地做到这一点,我可能会一个计算列PaymentMonth添加到表:

ALTER TABLE dbo.Payments ADD PaymentMonth AS MONTH(PaymentDate) PERSISTED 

它坚持并存储在表 - 所以真的没有性能开销查询它。这是一个4字节的INT值 - 所以空间开销也很小。

一旦你的,你可以简化您的查询是沿着线的东西:

SELECT ItemID, IsPaid, 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 And PaymentMonth = 1 AND UserID = 100) AS 'Jan', 
(SELECT SUM(Amount) FROM Payments WHERE Year = 2010 And PaymentMonth = 2 AND UserID = 100) AS 'Feb', 
.... and so on ..... 
FROM LIVE L 
INNER JOIN Payments I ON I.LiveID = L.RECORD_KEY 
WHERE UserID = 16178 
+0

是否有性能开销?即使在低规格框中,SQL Server也可以每秒执行数百万次'CONVERT(NVARCHAR(7),PaymentDate,120)'。我不确定使用计算列将会带来什么好处(除非您可能会将其编入索引) – NickG

0

现在查询被明确地望着只支付了一年= 2010,但是,我想你的意思是你的1月/ 2月/ 3月实际上代表了2009年。如果是这样,你需要对此稍微调整一下。不要重新查询每列的总和值,只需要以月份为单位的日期差异条件。将剩下的部分放在WHERE子句中。

SELECT 
     SUM(case when DateDiff(m, PaymentDate, @start) = 0 
      then Amount else 0 end) AS "Apr", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 1 
      then Amount else 0 end) AS "May", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 2 
      then Amount else 0 end) AS "June", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 3 
      then Amount else 0 end) AS "July", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 4 
      then Amount else 0 end) AS "Aug", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 5 
      then Amount else 0 end) AS "Sep", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 6 
      then Amount else 0 end) AS "Oct", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 7 
      then Amount else 0 end) AS "Nov", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 8 
      then Amount else 0 end) AS "Dec", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 9 
      then Amount else 0 end) AS "Jan", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 10 
      then Amount else 0 end) AS "Feb", 
     SUM(case when DateDiff(m, PaymentDate, @start) = 11 
      then Amount else 0 end) AS "Mar" 
    FROM 
     Payments I 
     JOIN Live L 
      on I.LiveID = L.Record_Key 
    WHERE 
      Year = 2010 
     AND UserID = 100 
11

限制NVARCHAR的尺寸至7,供给到转换成只显示“YYYY-MM”

SELECT CONVERT(NVARCHAR(7),PaymentDate,120) [Month], SUM(Amount) [TotalAmount] 
FROM Payments 
GROUP BY CONVERT(NVARCHAR(7),PaymentDate,120) 
ORDER BY [Month] 
1

的另一种方法,不涉及添加列到的结果,是只需简单地将日期的day组成部分清零,那么2016-07-132016-07-16都将是2016-07-01 - 因此会使它们按月相等。

如果你有date(不是datetime)值,那么你就可以直接零它:

SELECT 
    DATEADD(day, 1 - DATEPART(day, [Date]), [Date]), 
    COUNT(*) 
FROM 
    [Table] 
GROUP BY 
    DATEADD(day, 1 - DATEPART(day, [Date]), [Date]) 

如果你有datetime价值,就需要使用CONVERT删除时间OF-天部分:

SELECT 
    DATEADD(day, 1 - DATEPART(day, [Date]), CONVERT(date, [Date])), 
    COUNT(*) 
FROM 
    [Table] 
GROUP BY 
    DATEADD(day, 1 - DATEPART(day, [Date]), CONVERT(date, [Date])) 
5

我喜欢结合DATEADDDATEDIFF功能是这样的:

GROUP BY DATEADD(MONTH, DATEDIFF(MONTH, 0, Created),0) 

总之,这两个函数为零时的日期分量比指定的日期部分较小(即MONTH在这个例子中)。

您可以将datepart位更改为YEARWEEK,DAY等......这是非常方便的。你原来的SQL查询会看起来像这样(我不能测试它,因为我没有你的数据集,但它应该把你放在正确的轨道上)。

DECLARE @start [datetime] = '2010-04-01'; 

SELECT 
    ItemID, 
    UserID, 
    DATEADD(MONTH, DATEDIFF(MONTH, 0, Created),0) [Month], 
    IsPaid, 
    SUM(Amount) 
FROM LIVE L 
INNER JOIN Payments I ON I.LiveID = L.RECORD_KEY 
WHERE UserID = 16178 
AND PaymentDate > @start 

一两件事:在Month列的类型是DateTime这也是一个很好的优势,如果你需要进一步处理的数据或将其映射.NET对象为例。