填补时间空白

时间:2018-10-01 16:08:45

标签: sql sql-server tsql

我需要有关查询的帮助,该查询将填补一些缺失的时间(以分钟为单位)的空白,并保持当前状态。我在医院工作,我们试图在任何给定的时间了解医院急诊科的瓶颈。
我表中的数据如下所示:

Patient_Id      event_time             Event_Status_Name
98676249    2018-09-24 18:39:00.000    Expected
98676249    2018-09-24 19:17:00.000    Waiting for Triage
98676249    2018-09-24 19:28:00.000    In Triage
98676249    2018-09-24 19:29:00.000    Waiting for Room
98676249    2018-09-24 19:45:00.000    Waiting for Provider
98676249    2018-09-24 19:48:00.000    In Process
98676249    2018-09-24 21:02:00.000    Await IP Orders
98676249    2018-09-24 22:59:00.000    Await IP Bed
98676249    2018-09-25 21:44:00.000    Ready for Admit

这基本上告诉我患者什么时候进入特定状态。但是,我需要做的是填写丢失的分钟数,并保持其所在状态。例如,从2018-09-24 18:39:00.000到2018-09-24 19:16:00.000患者处于“预期”状态。仅以第一个状态为例,我所需的查询输出将看起来像这样:

Patient_Id  event_time             Event_Status_Name
98676249    2018-09-24 18:39:00.000    Expected
98676249    2018-09-24 18:40:00.000    Expected
98676249    2018-09-24 18:41:00.000    Expected
98676249    2018-09-24 18:42:00.000    Expected
98676249    2018-09-24 18:43:00.000    Expected
98676249    2018-09-24 18:44:00.000    Expected
98676249    2018-09-24 18:45:00.000    Expected
98676249    2018-09-24 18:46:00.000    Expected
98676249    2018-09-24 18:47:00.000    Expected
98676249    2018-09-24 18:48:00.000    Expected

等,直到我达到“等待分类”的下一个状态 ...然后我需要针对该状态的每一分钟执行相同的操作,直到下一个...等等。等

如何编写不执行循环的查询?有数百万条记录(和分钟)要考虑,所以我需要一个便宜的查询。

感谢您的帮助!

3 个答案:

答案 0 :(得分:0)

您可以在此处使用递归CTE。作为锚,获取所有现有行以及按每个患者时间戳排序的行号。然后从锚点中选择行,然后通过行号和患者对他们应用下一个事件,并检查下一个分钟是否小于后续事件的时间戳。

WITH cte
AS
(
SELECT x.patient_id,
       x.event_time,
       x.event_status_name,
       x.rn
       FROM (SELECT t.patient_id,
                    t.event_time,
                    t.event_status_name,
                    row_number() OVER (PARTITION BY t.patient_id
                                       ORDER BY t.event_time) rn
                    FROM elbat t) x
UNION ALL
SELECT c.patient_id,
       dateadd(minute, 1, c.event_time),
       c.event_status_name,
       c.rn
       FROM cte c
            CROSS APPLY (SELECT y.patient_id,
                                y.event_time
                                FROM (SELECT t.patient_id,
                                             t.event_time,
                                             row_number() OVER (PARTITION BY t.patient_id
                                                                ORDER BY t.event_time) rn
                                             FROM elbat t) y
                                     WHERE y.rn = c.rn + 1) x
        WHERE x.patient_id = c.patient_id
              AND x.event_time > dateadd(minute, 1, c.event_time)
)
SELECT *
       FROM cte c
       ORDER BY c.patient_id,
                c.event_time
OPTION (MAXRECURSION 1364);

但是有一个问题。您的差距是如此之大,以至于超过了最大递归级别100。您可以使用OPTION (MAXRECURSION n)进行放大。要找到合适的n,您可以查询数据以查找几分钟后发生事件的最大差异。再次使用row_number()映射后续事件。将该最大值减去1表示最大递归级别。

WITH cte
AS
(
SELECT t.patient_id,
       t.event_time,
       row_number() OVER (PARTITION BY t.patient_id
                          ORDER BY event_time) rn
       FROM elbat t
)
SELECT max(datediff(minute, c2.event_time, c1.event_time)) - 1
       FROM cte c1
            INNER JOIN cte c2
                       ON c2.patient_id = c1.patient_id
                          AND c2.rn = c1.rn - 1;

db<>fiddle

答案 1 :(得分:0)

您是否不认为插入这些分钟将使您的数百万行变得更多,其中包含大量冗余数据的行呢?目的是什么? (也许某种与显示相关的工作?)。

无论如何,如果您认为必须这样做,则可以通过交叉应用来实现。即:

WITH tally
AS (SELECT TOP (1440 * 10)
           ROW_NUMBER() OVER (ORDER BY t1.object_id) AS N
    FROM master.sys.all_columns t1
        CROSS JOIN master.sys.all_columns t2),
     missedMinutes
AS (SELECT *
    FROM dbo.Patients t1
        CROSS APPLY
    (
        SELECT DATEDIFF(MINUTE, t1.event_time, MIN(t2.event_time))
        FROM Patients t2
        WHERE t1.Patient_Id = t2.Patient_Id
              AND t2.event_time > t1.event_time
    ) t(missed)
        CROSS APPLY
    (
        SELECT TOP (ISNULL(missed, 1) - 1)
               DATEADD(MINUTE, N, t1.event_time)
        FROM dbo.Patients t2
            CROSS JOIN tally
        WHERE t2.Patient_Id = t1.Patient_Id
              AND t1.event_time = t2.event_time
        ORDER BY tally.N
    ) tt(missing) )
SELECT Patient_Id,
       missing AS event_time,
       Event_Status_Name
INTO #missInsert
FROM missedMinutes
ORDER BY event_time;

SELECT *
FROM #missInsert;

DROP TABLE #missInsert;

DBFiddle Demo here

答案 2 :(得分:0)

 <link type="stylesheet" href="yourstyle.css" />
<style>
 * {
  color: #333 !important;
 }
</style>