【问题标题】:Minutes between dates when multiple records多条记录时的日期之间的分钟数
【发布时间】:2019-03-26 18:44:06
【问题描述】:

表“审计”具有字段 id、old_status、new_status 和 changed_at。状态包含值 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')) 

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

是否有会返回该实际时间的查询?

【问题讨论】:

  • 这听起来像是典型的“孤岛和缝隙”问题。
  • 当记录的最后状态为“保留”时,您的预期结果是什么?据推测,您不会遇到相反的问题(记录的 first 状态处于保留状态)。
  • 请提供样本数据和期望的结果。
  • 如果记录当前处于保留状态,我希望将其保留到现在之间的时间作为计算的一部分

标签: sql sql-server ssms-2017


【解决方案1】:

对于下面的答案,我假设记录的初始/默认状态是“打开”。我还假设您的 SQL Server 版本具有 LEAD/LAG 功能。

假设您有关于 3 条记录的信息。所有 3 条记录都以“打开”状态开始。

  • 对于记录 1,状态更改了 4 次:
    • 在 8:00 从 Open 更改为 On Hold。
    • 在 9:00 从暂停更改为开放。
    • 在 10:00 改回等待状态
    • 改为11:00关门。
  • 对于记录 2,状态已更改两次:
    • 在 8:00,它从 Open 更改为 On Hold。
    • 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 小时/120 分钟:从 8 点到 9 点 1 小时,然后从 10 点到 11 点又一个小时。
  • 记录 2 仅暂停 1 小时:从 8 点到 9 点。
  • 对于记录 3,不清楚您的预期结果是什么:结果会是从 8:00(被搁置时)到当前日期/时间吗?或者您想从结果中排除这个?

要开始解决问题,您可以先使用 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,从 Open 到 On Hold 的更改为空,因为这是第一次更改。

现在您可以将其包含在 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 |
+----+-----------------+

【讨论】:

  • 谢谢 Zack,这正是我所需要的。
【解决方案2】:

JOIN 表到自身 ON 条件是 JOIN 的左侧是“On Hold”,右侧是左侧之后的 TOP 1 记录,并且不是“On Hold”,并且具有与左侧相同的ID

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 2011-10-04
    • 2017-03-17
    相关资源
    最近更新 更多