如何按DATEDIFF分组行?

时间:2013-08-14 13:42:05

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

我希望你能帮助我。

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

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

    1. ID - PK
    2. STAFF_GUID - 员工ID
    3. LAST_UPDATED - 员工进入房间的日期时间
    4. 这是我到目前为止所得到的。这仅满足要求#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
      这满足了前一行,当前行和下一行之间间隔小于或等于10分钟的要求#1。

      你能帮我解决第二个要求吗?如果已应用,则返回的ID应仅为:
      13,14

1 个答案:

答案 0 :(得分:3)

这是一个想法。您不需要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