【问题标题】:How to group by month using SQL Server?如何使用 SQL Server 按月分组?
【发布时间】:2012-01-10 19:55:46
【问题描述】:

我有一个具有此架构的表

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 

但是当我应该获取值时,我只会得到空值。我错过了什么吗?

【问题讨论】:

  • 您是否尝试旋转表格以按年/月显示列,并在其下按用户 ID 显示付款总和?
  • 为什么您在 where 子句中的 UserID = 16178 与您的子查询 where 子句中的 UserID = 100 不同?还有一月、二月和三月的最后 3 个子查询,它们与四月的月差真的分别是 9、10 和 11 吗?

标签: sql sql-server-2008 datediff


【解决方案1】:
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

【讨论】:

  • 像这样在MySql中使用逗号group by year(date),month(date)
【解决方案2】:

将 NVARCHAR 的维度限制为 7,提供给 CONVERT 以仅显示“YYYY-MM”

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

【讨论】:

    【解决方案3】:

    我更喜欢像这样组合 DATEADDDATEDIFF 函数:

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

    这两个函数一起将日期分量小于归零,而不是指定的datepart(即本例中的MONTH)。

    您可以将datepart 位更改为YEARWEEKDAY 等...非常方便。

    您的原始 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 对象,这也是一个很好的优势。

    【讨论】:

      【解决方案4】:

      如果您需要经常这样做,我可能会在表中添加一个计算列 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 
      

      【讨论】:

      • 是否存在性能开销?即使在低规格的机器上,SQL Server 每秒也可以处理数百万CONVERT(NVARCHAR(7),PaymentDate,120)。我不确定使用计算列是否会带来任何好处(除非您可能要对其进行索引)
      【解决方案5】:
      DECLARE @start [datetime] = 2010/4/1;
      

      应该是……

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

      您拥有的是将 2010 除以 4,然后除以 1,然后转换为日期。这是从 1900 年 1 月 1 日开始的第 57.5 天。

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

      【讨论】:

        【解决方案6】:

        另一种不涉及在结果中添加列的方法是简单地将日期的 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 删除时间部分:

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

        【讨论】:

          【解决方案7】:

          现在您的查询明确只查看年份 = 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
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-01-25
            • 2020-11-11
            • 2021-12-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多