【问题标题】:How to group rows by their DATEDIFF?如何按 DATEDIFF 对行进行分组?
【发布时间】:2013-08-14 13:42:04
【问题描述】:

我希望你能帮助我。

我需要在 HH_Solution_Audit 表中显示记录 -- 如果 2 名或更多员工在 10 分钟内进入房间。以下是要求:

  1. 仅显示时间戳 (LAST_UPDATED) 间隔小于或等于 10 分钟的事件。因此,我必须将当前行与下一行和上一行进行比较,以检查它们的 DATEDIFF 是否小于或等于 10 分钟。我已经完成了这部分。
  2. 仅当房间内不同 STAFF_GUID 的数量小于或等于 10 分钟至少为 2 时才显示记录。

HH_Solution_Audit 表详细信息:

  1. ID - PK
  2. STAFF_GUID - 员工 ID
  3. LAST_UPDATED - 工作人员进入房间的日期时间

这是我到目前为止所得到的。这仅满足要求 #1。

CREATE TABLE HH_Solution_Audit (
ID INT PRIMARY KEY,
STAFF_GUID NVARCHAR(1),
LAST_UPDATED DATETIME
)
GO
INSERT INTO HH_Solution_Audit VALUES (1, 'b', '2013-04-25 9:01')
INSERT INTO HH_Solution_Audit VALUES (2, 'b', '2013-04-25 9:04')
INSERT INTO HH_Solution_Audit VALUES (3, 'b', '2013-04-25 9:13')
INSERT INTO HH_Solution_Audit VALUES (4, 'a', '2013-04-25 10:15')
INSERT INTO HH_Solution_Audit VALUES (5, 'a', '2013-04-25 10:30')
INSERT INTO HH_Solution_Audit VALUES (6, 'a', '2013-04-25 10:33')
INSERT INTO HH_Solution_Audit VALUES (7, 'a', '2013-04-25 10:41')
INSERT INTO HH_Solution_Audit VALUES (8, 'a', '2013-04-25 11:02')
INSERT INTO HH_Solution_Audit VALUES (9, 'a', '2013-04-25 11:30')
INSERT INTO HH_Solution_Audit VALUES (10, 'a', '2013-04-25 11:45')
INSERT INTO HH_Solution_Audit VALUES (11, 'a', '2013-04-25 11:46')
INSERT INTO HH_Solution_Audit VALUES (12, 'a', '2013-04-25 11:51')
INSERT INTO HH_Solution_Audit VALUES (13, 'a', '2013-04-25 12:24')
INSERT INTO HH_Solution_Audit VALUES (14, 'b', '2013-04-25 12:27')
INSERT INTO HH_Solution_Audit VALUES (15, 'b', '2013-04-25 13:35')

    DECLARE @numOfPeople INT = 2,   
              --minimum number of people that must be inside 
              --the room for @lengthOfStay minutes
            @lengthOfStay INT = 10, 
              --number of minutes of stay
            @dateFrom DATETIME = '04/25/2013 00:00',
            @dateTo DATETIME = '04/25/2013 23:59';
    WITH cteSource AS
    (
         SELECT ID, STAFF_GUID, LAST_UPDATED, 
              ROW_NUMBER() OVER (ORDER BY LAST_UPDATED) AS row_num
         FROM HH_SOLUTION_AUDIT 
              WHERE LAST_UPDATED >= @dateFrom AND LAST_UPDATED <= @dateTo
    )
    SELECT [current].ID, [current].STAFF_GUID, [current].LAST_UPDATED
    FROM
         cteSource AS [current]
    LEFT OUTER JOIN
         cteSource AS [previous] ON [current].row_num = [previous].row_num + 1
    LEFT OUTER JOIN
         cteSource AS [next] ON [current].row_num = [next].row_num - 1
    WHERE
         DATEDIFF(MINUTE, [previous].LAST_UPDATED, [current].LAST_UPDATED) 
         <= @lengthOfStay
         OR
         DATEDIFF(MINUTE, [current].LAST_UPDATED, [next].LAST_UPDATED) 
         <= @lengthOfStay  
    ORDER BY [current].ID, [current].LAST_UPDATED    

运行查询返回 ID:
1、2、3、5、6、7、10、11、12、13、14
这满足了第 1 条要求,即前一行、当前行和下一行之间的间隔小于或等于 10 分钟。

你能帮我解决第二个要求吗?如果应用了,返回的 ID 应该只有:
13, 14

【问题讨论】:

  • 你能设置一个示例数据吗?几行和预期的结果?
  • 这个问题真是神了,我看你也很努力,但是如果你举一些例子,我们可以帮助你更多。谢谢!
  • @NenadZivkovic 嗨内纳德。我按照您的要求添加了示例数据和预期结果。谢谢你。我忘记了。
  • @GianlucaBobbio 嗨 Gian。我添加了更多有用的数据。请看一看。谢谢你的客气话。

标签: sql sql-server sql-server-2008 tsql


【解决方案1】:

这是一个想法。您不需要 ROW_NUMBER 以及上一条和下一条记录。您只需要联合查询 - 一个寻找有人检查 X 分钟后的每个人,另一个寻找 X 分钟前的人。每个都使用相关的子查询和 COUNT(*) 来查找匹配的人数。如果数字大于您的@numOfPeople - 就是这样。

编辑: 新版本:我们不会在前后 10 分钟内进行两次查询,而是只检查 10 分钟后的时间 - 选择在 cteLastOnes 中匹配的那些。之后将进入查询的另一部分以搜索那 10 分钟内实际存在的那些。最终再次将他们和“最后的人”联合起来

WITH cteSource AS
(
    SELECT ID, STAFF_GUID, LAST_UPDATED
    FROM HH_SOLUTION_AUDIT 
    WHERE LAST_UPDATED >= @dateFrom AND LAST_UPDATED <= @dateTo
)
,cteLastOnes AS 
(
    SELECT * FROM cteSource c1
    WHERE @numOfPeople -1 <= (SELECT COUNT(DISTINCT STAFF_GUID) 
                              FROM cteSource c2 
                              WHERE DATEADD(MI,@lengthOfStay,c2.LAST_UPDATED) > c1.LAST_UPDATED 
                                AND C2.LAST_UPDATED <= C1.LAST_UPDATED 
                                AND c1.STAFF_GUID <> c2.STAFF_GUID) 
)
SELECT * FROM cteLastOnes
UNION
SELECT * FROM cteSource s
WHERE EXISTS (SELECT * FROM cteLastOnes l 
               WHERE DATEADD(MI,@lengthOfStay,s.LAST_UPDATED) > l.LAST_UPDATED 
                AND s.LAST_UPDATED <= l.LAST_UPDATED 
                AND s.STAFF_GUID <> l.STAFF_GUID)

SQLFiddle DEMO - new version

SQLFiddle DEMO - old version

【讨论】:

  • 非常感谢内纳德!我喜欢它的简单!
  • @Raii NP。您可能希望将 COUNT(*) 替换为 COUNT(Distinct STAFF_GUID) - 以确保只计算不同的员工。在此示例中无关紧要,但如果您的 @numOfPeople 大于 2,它可能会有所不同。
  • 内纳德,我明白了。我将围绕您的脚本来查看“COUNT”的差异。我喜欢它的逻辑。没想到那样做我的任务。再次感谢。
  • 嗨,Nenad,如果@numOfPeople >2,您的脚本将不起作用。我尝试编辑您的代码,但没有运气。错误是:例如结果应该是4,只会返回3。它消除了重复的 STAFF_GUID 或重复之前或之后的值。
  • @Raii 我明白了,问题是如果 3 行在 10 分钟内 - 中间那行没有被选中,因为它前面和后面都没有 2 行。稍后我会重写查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
相关资源
最近更新 更多