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 点以后下的任何订单都不会被考虑在内。
根据我迄今为止所做的研究,我相信我应该使用DATEADD
、DATEDIFF
和COUNT
的某种组合(而不是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;
注意使用>= AND <
表示半开区间。这确保包括最后一天的整个时间。
【讨论】:
非常感谢您的回答!我还没有遇到'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:给定年份和月份,我如何计算每周的订单数的主要内容,如果未能解决你的问题,请参考以下文章