【问题标题】:SQL Server - Selecting Frequent Records Within Time FrameSQL Server - 选择时间范围内的频繁记录
【发布时间】:2020-09-28 15:24:13
【问题描述】:

我正在寻找一种方法来选择其中一列具有相同记录且日期时间在 5 分钟内的所有记录。我管理一个票务系统,并试图查看在 5 分钟时间内创建的所有类似票证。请参阅下面的示例数据集:

|ID |Subject    |CreatedDateTime    |
-------------------------------------
|1  |A          |2020-09-28 11:01:00|
|2  |A          |2020-09-28 11:02:00|
|3  |A          |2020-09-28 11:03:00|
|4  |A          |2020-09-28 11:03:09|
|5  |A          |2020-09-28 11:04:52|
|6  |A          |2020-09-28 11:15:00|
|7  |B          |2020-09-28 11:20:00|
|8  |B          |2020-09-28 11:20:00|
|9  |B          |2020-09-28 11:20:00|

我的目标是只选择记录 1-5,因为 5 个相同的主题并且它们都是在 5 分钟内创建的。 6-10不应选择,因为Subject数量不够多,或者超出了规定的时间范围。

以下是我到目前为止的一个测试查询,但它没有考虑 5 分钟的范围(我只回顾 1 周,因此该子句):

SELECT Subject,COUNT(*)
FROM TableName
WHERE CreatedDateTime > DATEADD(day, -7, GETDATE())
GROUP BY Subject
HAVING COUNT(*) > 5 
ORDER BY COUNT(*) DESC;

有没有什么方法可以只在接近的时间范围内看到类似的记录?提前谢谢大家!

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    您可以为每条记录提前和落后,找出以分钟为单位的时差,然后仅保留在 5 分钟内与至少一条其他记录相连的记录:

    WITH cte AS (
        SELECT *,
               DATEDIFF(minute,
                   LAG(CreatedDateTime) OVER (PARTITION BY Subject
                                              ORDER BY CreatedDateTime),
                   CreatedDateTime) AS LagCreatedDateTime,
               DATEDIFF(minute,
                   CreatedDateTime,
                   LEAD(CreatedDateTime) OVER (PARTITION BY Subject
                                               ORDER BY CreatedDateTime))
                    AS LeadCreatedDateTime
        FROM TableName
    )
    
    SELECT *
    FROM cte
    WHERE LagCreatedDateTime <= 5 OR LeadCreatedDateTime <= 5;
    

    有关使用示例数据运行的示例,请参阅下面的演示链接。

    Demo

    【讨论】:

    • 嗨蒂姆,感谢您的回复。当我运行此查询时(更新 TableName 之后),我在第 9 行的关键字“AS”附近收到语法错误。此外,当我注释掉 LeadCreatedDateTime 的所有内容时,我得到了几个在数据库中只有一条记录的结果。当我添加AND Subject IN (previousQueryFromAbove) 时更好,但仍有一些独立的主题只出现一次。
    • @AndrewDraper 抱歉,我那里缺少括号。请尝试更正后的更新答案,这似乎在上面的演示链接中有效。
    • 谢谢你,成功了。我最终在我之前的评论中添加了查询以进行一些额外的过滤,但是您列出的内容几乎正是我所需要的。非常感谢!
    【解决方案2】:

    如果您要对相隔 5 分钟内的记录进行分组,则可以使用递归查询来“分组”日期范围,如下所示

    with data
      as (select *,row_number() over(partition by subject order by createddatetime) as rnk
            from t
            )
        ,cte
        as(select id,subject,createddatetime as begin_date, createddatetime,cast(1 as int) as grp
             from data
           where rnk=1  
           union all
           select b.id
                 ,b.subject
                 ,b.createddatetime
                 ,case when datediff(minute,a.createddatetime,b.createddatetime) > 5 then 
                        b.createddatetime
                       else
                        a.createddatetime
                   end as createddatetime
                 ,case when datediff(minute,a.createddatetime,b.createddatetime) > 5 then 
                        a.grp+1
                       else
                        a.grp
                   end as grp  
             from cte a
             join t b
               on a.id+1=b.id
              and a.subject=b.subject 
           )
         select * from cte  order by 1
    
     +----+---------+-------------------------+-------------------------+-----+
    | id | subject |       begin_date        |     createddatetime     | grp |
    +----+---------+-------------------------+-------------------------+-----+
    |  1 | A       | 2020-09-28 11:01:00.000 | 2020-09-28 11:01:00.000 |   1 |
    |  2 | A       | 2020-09-28 11:02:00.000 | 2020-09-28 11:01:00.000 |   1 |
    |  3 | A       | 2020-09-28 11:03:00.000 | 2020-09-28 11:01:00.000 |   1 |
    |  4 | A       | 2020-09-28 11:03:09.000 | 2020-09-28 11:01:00.000 |   1 |
    |  5 | A       | 2020-09-28 11:04:52.000 | 2020-09-28 11:01:00.000 |   1 |
    |  6 | A       | 2020-09-28 11:15:00.000 | 2020-09-28 11:15:00.000 |   2 |
    |  6 | A       | 2020-09-28 11:17:00.000 | 2020-09-28 11:17:00.000 |   2 |
    |  7 | B       | 2020-09-28 11:20:00.000 | 2020-09-28 11:20:00.000 |   1 |
    |  8 | B       | 2020-09-28 11:20:00.000 | 2020-09-28 11:20:00.000 |   1 |
    |  9 | B       | 2020-09-28 11:20:00.000 | 2020-09-28 11:20:00.000 |   1 |
    +----+---------+-------------------------+-------------------------+-----+
    

    db 小提琴链接

    https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=7fabc2d2753c2ab01bb40f58d82bf222

    【讨论】:

    • 嗨乔治,我用正确的表名编辑了你给我的查询,但我不知道这是否返回了正确的结果。在上面列出的结果中,ID 6-9 正在返回,但记录 6 不在另一条记录的 5 分钟内,并且 7-9 只有 3 条记录,因此不应返回
    • 您会发现我的查询按 (subject,grp) 组合分组。例如:(A,1) 将记录 id 1-5 分组在一起,因为它们都在 11:01 的前 5 分钟内。 (A,2) 将下一组 5 分钟间隔组合在一起,即 (11:15,11:17)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    相关资源
    最近更新 更多