对于下面的答案,我假设记录的初始/默认状态是“打开”。我还假设您的 SQL Server 版本具有 LEAD/LAG 功能。
假设您有关于 3 条记录的信息。所有 3 条记录都以“打开”状态开始。
- 对于记录 1,状态更改了 4 次:
- 它在 8:00 从 Open 更改为 On Hold。
- 它在 9:00 由 On Hold 更改为 Open。
- 10:00 改回 On Hold
- 改为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 |
+----+-----------------+