【问题标题】:Number of Shifts betweens two times两次之间的班次次数
【发布时间】:2019-02-24 00:50:28
【问题描述】:

您能帮我解决以下问题吗?

我正在尝试计算患者在医院的轮班次数。轮班时间从7:00 AM6:59 PM7:00 PM6.59 AM

如果患者在轮班开始后入住某个地点,我们会在总计算中忽略该轮班。

这是示例数据以及最终结果的样子:

DECLARE @T AS TABLE
(
    ID          INT,
    LOCATION VARCHAR(10),
    Date_entered DATETIME,
    date_left datetime
);

DECLARE @endresult AS TABLE
(
    ID          INT,
    LOCATION VARCHAR(10),
    Date_entered DATETIME,
    date_left datetime,
    Total_shifts int
)


insert into @T VALUES
(1,'AB','01/01/2019 07:10','01/01/2019 20:30'), 
(2,'CD','01/01/2019 20:30','01/04/2019 02:30'), 
(3,'EF','01/04/2019 02:30','01/07/2019 19:30'), 
(4,'GH','01/07/2019 19:30','01/08/2019 13:30')


insert into @endresult VALUES
(1,'AB','01/01/2019 07:10','01/01/2019 20:30',1), 
(2,'CD','01/01/2019 20:30','01/04/2019 02:30',4), 
(3,'EF','01/04/2019 02:30','01/07/2019 19:30',8), 
(4,'GH','01/07/2019 19:30','01/08/2019 13:30',1) 

SELECT * FROM @t

select * from @endresult

我尝试使用递归 CTE,但查询花费了太多时间才能完成。有什么简单的计算时间的方法吗?

【问题讨论】:

    标签: sql tsql sql-server-2012


    【解决方案1】:

    下面是一个查询,它为您的示例数据返回正确的结果:

    select 
        t.*, 
        DATEDIFF(DAY, date_entered, date_left) * 2
            - CASE WHEN DATEPART(HOUR, date_entered) < 7 THEN 0 WHEN DATEPART(HOUR, date_entered) < 19 THEN 1 ELSE 2 END
            + CASE WHEN DATEPART(HOUR, date_left)    < 7 THEN 0 WHEN DATEPART(HOUR, date_left)    < 19 THEN 1 ELSE 2 END
        AS Total_shifts
    from @t t;
    

    逻辑是先计算进出之间的天数,然后乘以 2 得到原始的班次数;然后通过检查患者进入和退出的时间来调整原始计数。

    这个 demo on DB Fiddle 与您的示例数据返回:

    身份证 |位置 |输入日期 |日期左 |总班次 -: | :------- | :----------------- | :----------------- | ------------: 1 | AB | 01/01/2019 07:10:00 | 2019 年 1 月 1 日 20:30:00 | 1 2 |光盘 | 2019 年 1 月 1 日 20:30:00 | 04/01/2019 02:30:00 | 4 3 |英孚 | 04/01/2019 02:30:00 | 2019 年 7 月 1 日 19:30:00 | 8 4 |生长激素 | 2019 年 7 月 1 日 19:30:00 | 2019 年 8 月 1 日 13:30:00 | 1

    【讨论】:

    • 考虑到所提供的场景,这是一个非常好的解决方案。它应该小于 6 还是 7?,根据样本数据,输出无论如何都是一样的。
    • 谢谢@Avi!啊是的你是对的,应该是7(和19),让我更新我的答案......
    • @GMB 非常感谢您的回答。修复了我的问题,查询也很快完成。再次感谢。
    【解决方案2】:

    这是一个可以为您提供所需结果的查询。它使用 3 个 CTE,第一个简单地选择最小 Date_entered 和最大 date_left 值,第二个将最小 Date_entered 值与班次开始时间(上午 7 点或下午 7 点)对齐,第三个递归 CTE 生成一个列表最小Date_entered 和最大date_left 值之间的所有班次开始时间。最后,我们将该 CTE 加入到 admissions 表中,并计算 Date_entereddate_left 之间的班次开始次数:

    WITH cte AS 
        (SELECT MIN(Date_entered) AS min_date, MAX(date_left) AS max_date
         FROM T),
        cte1 AS
       (SELECT CASE WHEN DATEPART(hour, min_date) < 7 THEN DATEADD(hour, 19, DATEADD(day, -1, CONVERT(DATE, min_date)))
                    ELSE DATEADD(hour, 7, CONVERT(DATETIME, CONVERT(DATE, min_date)))
                    END AS min_shift
        FROM cte),
        shifts AS 
       (SELECT min_shift AS shift_start
        FROM cte1
        UNION ALL 
        SELECT DATEADD(hour, 12, shift_start)
        FROM shifts
        WHERE shift_start < (SELECT max_date FROM cte))
    SELECT T.ID, T.LOCATION, T.Date_Entered, T.date_left, COUNT(s.shift_start) AS Total_shifts
    FROM T
    JOIN shifts s ON s.shift_start BETWEEN T.Date_Entered AND T.date_left
    GROUP BY T.ID, T.LOCATION, T.Date_Entered, T.date_left
    

    输出:

    ID  LOCATION    Date_Entered            date_left               Total_shifts
    1   AB          01/01/2019 07:10:00     01/01/2019 20:30:00     1
    2   CD          01/01/2019 20:30:00     04/01/2019 06:59:00     4
    3   EF          04/01/2019 07:00:00     07/01/2019 19:30:00     8
    4   GH          07/01/2019 19:30:00     08/01/2019 13:30:00     1
    

    Demo on dbfiddle

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多