SQL:给定年份和月份,我如何计算每周的订单数

Posted

技术标签:

【中文标题】SQL:给定年份和月份,我如何计算每周的订单数【英文标题】:SQL : given a year and month how can I count the number of orders per WEEK 【发布时间】:2022-01-10 17:57:03 【问题描述】:

我的任务是返回给定年份和月份内“每周”的订单数量(这一切都发生在 SSMS 中)。

我的数据如下所示:

OrderId DateCreated
1 2021-12-04 06:01:14.6333333
2 2021-12-04 07:01:14.6333333
3 2021-12-24 00:00:00.0000000
4 2021-12-31 06:01:14.6333333
5 2021-12-31 06:01:14.6333333

我想让结果表看起来像这样:

Week OrdersCount
1 1
2 0
3 0
4 1
5 2

目前我有以下 SQL 存储过程,它以年份 (@year) 和月份 (@month) 作为参数:

SELECT
    SUM(CASE WHEN DateCreated BETWEEN (DATEFROMPARTS(@year, @month, 01)) AND (DATEFROMPARTS(@year, @month, 07)) 
             THEN 1 ELSE 0 END) AS Week1,
    SUM(CASE WHEN DateCreated BETWEEN (DATEFROMPARTS(@year, @month, 08)) AND (DATEFROMPARTS(@year, @month, 14)) 
             THEN 1 ELSE 0 END) AS Week2,
    SUM(CASE WHEN DateCreated BETWEEN (DATEFROMPARTS(@year, @month, 15)) AND (DATEFROMPARTS(@year, @month, 21)) 
             THEN 1 ELSE 0 END) AS Week3,
    SUM(CASE WHEN DateCreated BETWEEN (DATEFROMPARTS(@year, @month, 22)) AND (DATEFROMPARTS(@year, @month, 28)) 
             THEN 1 ELSE 0 END) AS Week4,
    SUM(CASE WHEN DateCreated BETWEEN (DATEFROMPARTS(@year, @month, 29)) AND (DATEFROMPARTS(@year, @month, 29)) 
             THEN 1 ELSE 0 END) AS Week5
FROM
    dbo.Orders

上述语句返回的内容与我需要的内容接近,但存在一些问题,我的结果集如下所示:

wk1 wk2 wk3 wk4 wk5
1 1 0 0 1 0

所以最大的问题当然是第 5 周的方向和缺失的订单。我的周是沿 x 轴而不是 y 轴显示的,但似乎因为 EOMONTH() 函数默认时间戳到午夜为止,当月最后一天上午 12 点以后下的任何订单都不会被考虑在内。

根据我迄今为止所做的研究,我相信我应该使用DATEADDDATEDIFFCOUNT 的某种组合(而不是SUM,这样我就可以使用@987654333 @) 我对这些函数/语句如何独立工作有很好的理解,但是很难将它们组合在一起以达到我的目标。任何和所有的帮助将不胜感激!

【问题讨论】:

您使用的是哪个 rdms? mysql SQL 服务器? 我正在使用 SSMS。不确定这是否回答了你的问题。 Microsoft SQL Server 管理系统 @nbk 请在上面发表评论。 ^ 使用日期的星期怎么样? datepart(week, DateCreated)? (取决于 DATEFIRST 设置) 还是加入 calendar table? Week5 有错字:应该是AND DATEFROMPARTS(@year, @month, 31) 【参考方案1】:

您可以使用CASE 获取一个星期编号,然后按该编号分组。

使用CROSS APPLY (VALUES避免重复代码

SELECT
    v.WeekNumber,
    TotalOrders = COUNT(*)
FROM
    dbo.Orders
CROSS APPLY (VALUES (
    CASE WHEN DateCreated >= DATEFROMPARTS(@year, @month, 1) AND DateCreated < DATEFROMPARTS(@year, @month, 8)
         THEN 1
         WHEN DateCreated >= DATEFROMPARTS(@year, @month, 8) AND DateCreated < DATEFROMPARTS(@year, @month, 15)
         THEN 2
         WHEN DateCreated >= DATEFROMPARTS(@year, @month, 15)) AND DateCreated < DATEFROMPARTS(@year, @month, 22)
         THEN 3
         WHEN DateCreated >= DATEFROMPARTS(@year, @month, 22)) AND DateCreated < DATEFROMPARTS(@year, @month, 29)
         THEN 4
         ELSE 5
    END
)) v(WeekNumber)

WHERE DateCreated >= DATEFROMPARTS(@year, @month, 1)
  AND DateCreated < DATEADD(month, 1, DATEFROMPARTS(@year, @month, 1))
GROUP BY
  v.WeekNumber;

注意使用&gt;= AND &lt; 表示半开区间。这确保包括最后一天的整个时间。

【讨论】:

非常感谢您的回答!我还没有遇到'CROSS APPLY (VALUES'),所以谢谢你。这是解决问题的好方法 你可能会觉得这篇文章很有趣***.com/a/65818648/14868997【参考方案2】:

我假设:

    您希望从每月的第一天开始计算第 1 周,并且始终是前 7 天,并且 第 5 周应始终是不完整的一周(即 28 日之后的月底),除非在非闰年的 2 月不存在。 您希望在结果集中看到零周。

如果所有这些假设都是正确的,我会使用这样的东西:

DECLARE @Weeks TABLE (Week INT IDENTITY(1,1), SD DATETIME, ED DATETIME)
DECLARE @Date DATETIME = DATEFROMPARTS(@year,@month,1)
WHILE @Date < EOMONTH(DATEFROMPARTS(@year,@month,1))
BEGIN
    INSERT INTO @Weeks (SD, ED) 
        SELECT @Date, CASE WHEN DATEADD(DAY,7,@Date) > DATEADD(MONTH,1,DATEFROMPARTS(@year,@month,1)) THEN DATEADD(MONTH,1,DATEFROMPARTS(@year,@month,1)) ELSE DATEADD(DAY,7,@Date) END
    SET @Date = DATEADD(DAY,7,@Date)
END

SELECT w.Week, COUNT(ID) 'OrdersCount'
FROM @Weeks w 
    LEFT JOIN dbo.Orders ON w.SD <= DateCreated AND w.ED > DateCreated
GROUP BY w.Week

我首先使用了一个表变量来构建一个周列表——这可能不是绝对必要的(还有其他方法可以实现),但我喜欢这个流程。我建议您使用

如果您不需要在所有情况下从每月的第一天开始计算周数,并且不介意不表示零周,那么一个非常简单的解决方案是使用 DATEPART(WEEK. ..),例如:

SELECT DATEPART(WEEK,DateCreated)-DATEPART(WEEK,DATEFROMPARTS(@year,@month,1))+1 'Week', COUNT(ID) 'CountID' 
    FROM dbo.Orders 
    WHERE DateCreated >= DATEFROMPARTS(@year,@month,1) AND DateCreated < DATEADD(MONTH,1,DATEFROMPARTS(@year,@month,1))
    GROUP BY DATEPART(WEEK,DateCreated)

【讨论】:

哇,这几乎完全符合我的要求。这是对“WHILE”的巧妙运用。我还没有真正玩过 SQL 中的循环,所以这是超级有用的,谢谢!【参考方案3】:

这可以简化 - 您需要做的是始终选择 5 周,然后外部联接到您计算每一行的周数的表。然后就是简单的统计一下每周的行数。

注意:这假设第 1 周始终是第 1 周到第 7 周,这不是我们通常计算一个月中周数的方式。 iso_week 从每月第一个星期四之前的星期一开始 - 美国的第 1 周包含每月的第一天到第一个星期六,其余的星期从星期日开始。

 --==== Some sample data
Declare @testData table (OrderId int, DateCreated datetime2);
 Insert Into @testData (OrderId, DateCreated)
 Values (1, '2021-12-04 06:01:14.6333333')
      , (2, '2021-12-04 07:01:14.6333333')
      , (3, '2021-12-24 00:00:00.0000000')
      , (4, '2021-12-31 06:01:14.6333333')
      , (5, '2021-12-31 06:01:14.6333333')
      , (1, '2021-11-04 06:01:14.6333333')
      , (2, '2021-11-09 07:01:14.6333333')
      , (3, '2021-11-18 00:00:00.0000000')
      , (4, '2021-11-28 06:01:14.6333333')
      , (5, '2021-11-30 06:01:14.6333333');

使用上面的示例数据 - 我们可以这样做:

 --==== Input parameters to select this year and this month
Declare @this_year int = 2021
      , @this_month int = 12;

 --==== Get the first of this month
Declare @start_month date = datefromparts(@this_year, @this_month, 1);

 --==== Force 5 weeks to be selected always (w.week_no) - left join to calculate week DateCreated falls into, join - group and count
 Select w.week_no
      , orders_count = count(t.week_no)
   From (Values (1), (2), (3), (4), (5))        As w(week_no)

   Left Join (Select week_no = datediff(day, @start_month, td.DateCreated) / 7 + 1
                From @testData                  As td
               Where td.DateCreated >= @start_month
                 And td.DateCreated <  dateadd(month, 1, @start_month)
              )                                 As t On t.week_no = w.week_no
  Group By
        w.week_no;

或者 - 我们可以使用 CTE

 --==== Alternate method using CTE instead of derived table
   With order_weeks
     As (
 Select week_no = datediff(day, @start_month, td.DateCreated) / 7 + 1  
   From @testData                               As td
  Where td.DateCreated >= @start_month
    And td.DateCreated <  dateadd(month, 1, @start_month)
        )
 Select w.week_no
      , orders_count = count(ow.week_no)
   From (Values (1), (2), (3), (4), (5))        As w(week_no)

   Left Join order_weeks                        As ow On ow.week_no = w.week_no
  Group By
        w.week_no;

这假设您只会选择特定年/月组合的数据。如果这需要能够选择一系列年/月 - 那么您需要为所有可能的范围添加年和月,并计算每个创建日期的年/月/周。

【讨论】:

以上是关于SQL:给定年份和月份,我如何计算每周的订单数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django 1.11 中查找给定月份每周的记录数?

我将如何在 php 中按给定月份和年份计算未来几个月

在 SQL 中为给定年份设置硬编码的日期和月份

Javascript:计算给定年份的月份天数

如何使用swift找到给定月份和年份的天数

SQL查询以获取特定年份的当前日期(考虑月份和日期)的数据