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

时间:2019-03-26 18:44:06

标签: sql sql-server ssms-2017

表“ audit”具有字段ID,old_status,new_status和changed_at。状态包含值打开,保留和关闭。我想确定审核保留的时间,简单计算:

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')) 

但是,现在我有一个审核记录,该记录被多次保留。上面的计算确实告诉我,从第一次进入保持状态到最后一次离开保持状态之间的总时间,但是并不能反映出它实际处于保持状态的时间。

是否有一个查询可以返回该实际时间?

2 个答案:

答案 0 :(得分:0)

在JOIN的左侧为'On Hold'且右侧为左侧之后的TOP 1记录且不为'On Hold'且具有ID与左侧相同。

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

答案 1 :(得分:0)

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

假设您有3条记录的信息。所有3条记录均以“打开”状态开头。

  • 对于记录1,状态已更改4次:
    • 从8:00的“开放”更改为“保留”。
    • 它已从“保留”更改为“ 9:00开启”。
    • 它已更改为10:00的保留状态
    • 已更改为11:00关闭。
  • 对于记录2,状态已更改两次:
    • 在8:00,它已从“打开”更改为“保留”。
    • 在9:00,它已更改为“已关闭”。
  • 记录3仅有一项更改:
    • 在8:00,它已从“打开”更改为“等待”(意味着当前状态为“等待”)。

以下是表格形式的数据:

+----+------------+------------+------------------+
| 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,从打开到保留的更改为空,因为这是第一个更改。

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