多个记录的日期之间的分钟数

Minutes between dates when multiple records

Table 'audit' 有字段 id、old_status、new_status 和 changed_at。 Status 包含值 Open、On Hold 和 Closed。我想确定审核搁置了多长时间,简单计算:

SELECT Datediff(minute, (SELECT Min(changed_at) 
                         FROM   audit 
                         WHERE  id = 123 
                                AND new_status = 'On Hold'), 
       (SELECT Max(changed_at) 
        FROM   audit 
        WHERE  id = 123 
               AND old_status = 
                   'On Hold')) 

但是,现在我有一个多次搁置的审计记录。上面的计算确实告诉我它第一次进入暂停状态和最后一次退出暂停状态之间的总时间,但它没有反映它处于暂停状态的实际时间。

是否有查询 return 那个实际时间?

JOIN table 自身 ON 条件是 JOIN 的左侧是 'On Hold' 右侧是左侧之后时间上的 TOP 1 记录,并且不是 'On Hold',和左边一样ID

由此,您的查询只需获取左侧记录和右侧记录之间的 DATEDIFF,以及 DATEDIFF 的 SUM,按 ID.

分组

对于下面的答案,我假设记录的 initial/default 状态为 "Open"。我还假设您的 SQL 服务器版本具有 LEAD/LAG 功能。

假设您有 3 条记录的信息。所有 3 条记录都以 "Open".

的状态开头
  • 对于记录 1,状态更改了 4 次:
    • 已在 8:00 从打开更改为暂停。
    • 已从暂停更改为在 9:00 打开。
    • 已在 10:00
    • 改回暂停
    • 已更改为在 11:00 关闭。
  • 对于记录 2,状态更改了两次:
    • 在 8:00,它从打开更改为暂停。
    • 在9:00,改为关闭。
  • 记录 3 只有一个变化:
    • 在 8:00,它从 Open 更改为 On Hold(表示当前状态为 On Hold)。

以下是表格形式的数据:

+----+------------+------------+------------------+
| id | old_status | new_status |    changed_at    |
+----+------------+------------+------------------+
|  1 | Open       | On Hold    | 2019-03-26 08:00 |
|  1 | On Hold    | Open       | 2019-03-26 09:00 |
|  1 | Open       | On Hold    | 2019-03-26 10:00 |
|  1 | On Hold    | Closed     | 2019-03-26 11:00 |
|  2 | Open       | On Hold    | 2019-03-26 08:00 |
|  2 | On Hold    | Closed     | 2019-03-26 09:00 |
|  3 | Open       | On Hold    | 2019-03-26 08:00 |
+----+------------+------------+------------------+

根据数据和我对您的问题的理解,您需要记录搁置的总时间。所以,对于上面的 3 条记录:

  • 记录 1 总共搁置了 2 hours/120 分钟:从 8 点到 9 点 1 小时,然后从 10 点到 11 点又过了一个小时。
  • 记录 2 仅保留 1 小时:从 8 到 9。
  • 对于记录 3,不清楚您的预期结果是什么:结果是从 8:00(当它被搁置时)到当前的 date/time?或者你想从你的结果中排除这个?

要开始解决问题,您可以先使用WINDOW函数查看相关结果。我最终使用了 LAG

首先,您可以使用 LAG 找出最后一次更改(记录)发生的位置:

SELECT 
    [id], 
    old_status,
    new_status,
    changed_at,
    prev_changed = LAG(changed_at) OVER
    (
        PARTITION BY [id]
        ORDER BY [id], changed_at
    )
FROM audit_records

这将为您提供以下结果:

+----+------------+------------+------------------+------------------+
| id | old_status | new_status |    changed_at    |   prev_changed   |
+----+------------+------------+------------------+------------------+
|  1 | Open       | On Hold    | 2019-03-26 08:00 | NULL             |
|  1 | On Hold    | Open       | 2019-03-26 09:00 | 2019-03-26 08:00 |
|  1 | Open       | On Hold    | 2019-03-26 10:00 | 2019-03-26 09:00 |
|  1 | On Hold    | Closed     | 2019-03-26 11:00 | 2019-03-26 10:00 |
|  2 | Open       | On Hold    | 2019-03-26 08:00 | NULL             |
|  2 | On Hold    | Closed     | 2019-03-26 09:00 | 2019-03-26 08:00 |
|  3 | Open       | On Hold    | 2019-03-26 08:00 | NULL             |
+----+------------+------------+------------------+------------------+

注意具有 NULL 值的记录:这些记录在 发生变化之前没有变化。因此对于记录 1,从打开到暂停的更改为空,因为这是第一个更改。

现在您可以将其包装在 CTE 中并计算分钟数:

WITH 
    audit_records_lead_lag([id], old_status, new_status, changed_at, prev_changed) AS
    (
        SELECT 
            [id], 
            old_status,
            new_status,
            changed_at,
            prev_changed = LAG(changed_at) OVER
            (
                PARTITION BY [id]
                ORDER BY [id], changed_at
            )
        FROM audit_records
    )
SELECT 
    [id], 
    minutes_in_hold = SUM(DATEDIFF(MINUTE, prev_changed, changed_at))
FROM audit_records_lead_lag
WHERE
    old_status = 'On Hold'
    AND prev_changed IS NOT NULL
GROUP BY [id]

结果如下:

+----+-----------------+
| id | minutes_in_hold |
+----+-----------------+
|  1 |             120 |
|  2 |              60 |
+----+-----------------+