SQL 按日期时间分组,最大差异为 x 分钟
Posted
技术标签:
【中文标题】SQL 按日期时间分组,最大差异为 x 分钟【英文标题】:SQL grouping by datetime with a maximum difference of x minutes 【发布时间】:2016-08-09 13:53:55 【问题描述】:我在 MS SQL Server 中对我的数据集进行分组时遇到问题。
我的桌子看起来像
# | CustomerID | SalesDate | Turnover
---| ---------- | ------------------- | ---------
1 | 1 | 2016-08-09 12:15:00 | 22.50
2 | 1 | 2016-08-09 12:17:00 | 10.00
3 | 1 | 2016-08-09 12:58:00 | 12.00
4 | 1 | 2016-08-09 13:01:00 | 55.00
5 | 1 | 2016-08-09 23:59:00 | 10.00
6 | 1 | 2016-08-10 00:02:00 | 5.00
现在我想将 SalesDate 与下一行的差异最大为 5 分钟的行分组。 所以第 1 行和第 2 行、第 3 行和第 4 行和第 5 行和第 6 行各为一组。
我的方法是使用 DATEPART() 函数获取分钟数并将结果除以 5:
(DATEPART(MINUTE, SalesDate) / 5)
对于第 1 行和第 2 行,结果将是 3,并且在这里分组可以完美地工作。 但是对于SalesDate的小时甚至一天部分发生变化的其他行,结果不能用于分组。
所以这就是我卡住的地方。如果有人能指出我正确的方向,我将不胜感激。
【问题讨论】:
检查this或this 您可以编写一个查询,使用 LEAD 或 LAG 来检查下一行并找到时间差,然后使用 DENSE_RANK 分配一个分组号,按差异 > 5 分钟进行分区。然后按该分组号分组。如果您要在一个查询中针对多行编写它,这是一个密集的过程。 我认为这是一个很好的第一个问题。 【参考方案1】:您希望根据它们之间的时间对相邻事务进行分组。这个想法是分配某种分组标识符,然后将其用于聚合。
这是一种方法:
使用lag()
和日期算术开始识别组。
做一个组的累计总和开始识别每个组。
聚合
查询如下所示:
select customerid, min(salesdate), max(saledate), sum(turnover)
from (select t.*,
sum(case when salesdate > dateadd(minute, 5, prev_salesdate)
then 1 else 0
end) over (partition by customerid order by salesdate) as grp
from (select t.*,
lag(salesdate) over (partition by customerid order by salesdate) as prev_salesdate
from t
) t
) t
group by customerid, grp;
【讨论】:
太棒了!非常感谢,这非常有效,甚至非常快! :)【参考方案2】:编辑
感谢@JoeFarrell 指出我回答了错误的问题。 OP 正在寻找行之间的动态时间差,但这种方法会创建固定的边界。
原答案
您可以创建一个时间表。这是一个包含一天中每一秒的一条记录的表。您的表格将有第二列,您可以使用它来执行分组。
CREATE TABLE [Time]
(
TimeId TIME(0) PRIMARY KEY,
TimeGroup TIME
)
;
-- You could use a loop here instead.
INSERT INTO [Time]
(
TimeId,
TimeGroup
)
VALUES
('00:00:00', '00:00:00'), -- First group starts here.
('00:00:01', '00:00:00'),
('00:00:02', '00:00:00'),
('00:00:03', '00:00:00'),
...
('00:04:59', '00:00:00'),
('00:05:00', '00:05:00'), -- Second group starts here.
('00:05:01', '00:05:00')
;
该方法在以下情况下效果最佳:
-
您需要在几个不同的查询中重复使用您的自定义分组。
您有两个或多个您经常使用的自定义组。
填充后,您可以简单地加入表并输出所需的结果。
/* Using the time table.
*/
SELECT
t.TimeGroup,
SUM(Turnover) AS SumOfTurnover
FROM
Sales AS s
INNER JOIN [Time] AS t ON t.TimeId = CAST(s.SalesDate AS Time(0))
GROUP BY
t.TimeGroup
;
【讨论】:
我认为这行不通。如果您有两个时间分别为 00:04:59 和 00:05:00 的条目,即使它们相隔一秒,您的解决方案也会将它们分开分组。这与 OP 在他自己的第一次尝试中报告的相同缺陷。 是的,你是对的。我误解了这个问题。 OP 不想要硬编码的边界。相反,他们正在寻找动态差异。以上是关于SQL 按日期时间分组,最大差异为 x 分钟的主要内容,如果未能解决你的问题,请参考以下文章