sql - SQL 更好的方法来查找两次之间的小时数
问题描述
我正在使用 SQL Server,并且我有一个包含班次的表,他们的开始/结束日期时间,我想找到这些班次的所有时间,这些时间在上午 9 点到下午 3 点之间。班次不得超过 24 小时(目前)。我已经编写了查询,它可以工作,但它很长。考虑到可以在不同日期开始或结束的轮班,它会变得更长。有没有更短、更有效的方法可以写出来?我写它是为了忽略 24 的限制,但仍然只考虑班次只会重叠 2 天的情况。
Select apc.in_punch_time, apc.out_punch_time,
case
when FORMAT(apc.in_punch_time,'HH:mm') <= '09:00' and FORMAT(apc.out_punch_time,'HH:mm') between '09:00' and '15:00' and DATEPART(day,apc.in_punch_time) = DATEPART(day,apc.out_punch_time) then
DATEDIFF(minute, DATETIMEFROMPARTS(YEAR(apc.out_punch_time),MONTH(apc.out_punch_time),day(apc.out_punch_time),9,0,0,0), apc.out_punch_time)
when FORMAT(apc.in_punch_time,'HH:mm') >= '09:00' and FORMAT(apc.out_punch_time,'HH:mm') <= '15:00' and DATEPART(day,apc.in_punch_time) = DATEPART(day,apc.out_punch_time) then
DATEDIFF(minute,apc.in_punch_time,apc.out_punch_time)
when FORMAT(apc.in_punch_time,'HH:mm') between '09:00' and '15:00' and FORMAT(apc.out_punch_time,'HH:mm') >= '15:00' and DATEPART(day,apc.in_punch_time) = DATEPART(day,apc.out_punch_time) then
DATEDIFF(minute,apc.in_punch_time, DATETIMEFROMPARTS(YEAR(apc.in_punch_time),MONTH(apc.in_punch_time),day(apc.in_punch_time),15,0,0,0))
when FORMAT(apc.in_punch_time,'HH:mm') <= '09:00' and FORMAT(apc.out_punch_time,'HH:mm') >= '15:00' and DATEPART(day,apc.in_punch_time) = DATEPART(day,apc.out_punch_time) then
360
when FORMAT(apc.in_punch_time,'HH:mm') < '09:00' and FORMAT(apc.out_punch_time,'HH:mm') <= '09:00' and DATEPART(day,apc.in_punch_time) <> DATEPART(day,apc.out_punch_time) then
360
when FORMAT(apc.in_punch_time,'HH:mm') between '9:00' and '15:00' and FORMAT(apc.out_punch_time,'HH:mm') <= '09:00' and DATEPART(day,apc.in_punch_time) <> DATEPART(day,apc.out_punch_time) then
DATEDIFF(minute, apc.in_punch_time, DATETIMEFROMPARTS(YEAR(apc.in_punch_time),MONTH(apc.in_punch_time),day(apc.in_punch_time),15,0,0,0))
when FORMAT(apc.in_punch_time,'HH:mm') <= '15:00' and FORMAT(apc.out_punch_time,'HH:mm') > '09:00' and DATEPART(day,apc.in_punch_time) <> DATEPART(day,apc.out_punch_time) then
DATEDIFF(minute, apc.in_punch_time, DATETIMEFROMPARTS(YEAR(apc.in_punch_time),MONTH(apc.in_punch_time),day(apc.in_punch_time),15,0,0,0)) +
DATEDIFF(minute, DATETIMEFROMPARTS(YEAR(apc.out_punch_time),MONTH(apc.out_punch_time),day(apc.out_punch_time),9,0,0,0), apc.out_punch_time)
when FORMAT(apc.in_punch_time,'HH:mm') >= '15:00' and FORMAT(apc.out_punch_time,'HH:mm') between '09:00' and '15:00' and DATEPART(day,apc.in_punch_time) <> DATEPART(day,apc.out_punch_time) then
DATEDIFF(minute, DATETIMEFROMPARTS(YEAR(apc.out_punch_time),MONTH(apc.out_punch_time),day(apc.out_punch_time),9,0,0,0), apc.out_punch_time)
when FORMAT(apc.in_punch_time,'HH:mm') = '00:00' and FORMAT(apc.out_punch_time,'HH:mm') = '00:00' and DATEPART(day,apc.in_punch_time) <> DATEPART(day,apc.out_punch_time) then
360
ELSE 0
END hours_between_9_3
from table apc
我的案例是:
同一天
- 班次在 9 点之前/9 点开始,在 9 点和 3 点之间结束
- 班次在 9 点/之后开始,在 3 点之前结束
- 班次从 9 点到 3 点开始,在 3 点/之后结束
- 班次在 9 点之前/9 点开始并在 3 点之后结束
不同的日子
- 轮班在第 1 天的 9 点之前开始,并在第 2 天的 9 点之前/9 点结束
- 班次从第 1 天的 9 点到 3 点开始,在第 2 天的 9 点/之前结束
- 班次从第 1 天的 9 点到 3 点开始,在第 2 天的 9 点之后结束
- 班次在第 1 天的 3 点/之后结束,在第 2 天的 9 点和 3 点之间结束
- 轮班从第 1 天的午夜开始,到第 2 天的午夜结束
感谢您的任何帮助。
解决方案
这是我的查询。它适用于任意天数。我要添加三个部分:
- 第一天 09:00 至 15:00 工作时间
- 最后一天 09:00 到 15:00 之间的工作时间(如果是第一天之后的另一天)
- 09:00 到 15:00 之间的工作时间,即 6 小时 = 360 分钟,在它们之间的任何日子
整件事读起来有点乏味(但是,写起来也很乏味)。
with data as
(
select
in_punch_time,
out_punch_time,
cast (in_punch_time as date) as start_date,
cast (out_punch_time as date) as end_date,
cast (in_punch_time as time) as start_time,
cast (out_punch_time as time) as end_time
from apc
)
select
in_punch_time,
out_punch_time,
-- first day
datediff
(
minute,
case when start_time < '09:00' then
'09:00'
when start_time > '15:00' then
'15:00'
else
start_time
end,
case when start_date < end_date then
'15:00'
when end_time > '15:00' then
'15:00'
when end_time < '09:00' then
'09:00'
else
end_time
end
) +
-- last day (if different from first day)
case when end_date > start_date and end_time > '09:00' then
datediff
(
minute,
'09:00',
case when end_time > '15:00' then
'15:00'
when end_time < '09:00' then
'09:00'
else
end_time
end
)
else
0
end +
-- between days (if any between first and last day)
case when datediff(day, start_date, end_date) > 1 then
(datediff(day, start_date, end_date) - 1) * 360 -- minutes
else
0
end as minutes_from_0900_to_1500
from data;
演示:https ://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=2318dc76adf43cd75e52b378b20b7cbc
推荐阅读
- query-optimization - 为什么即使在刚刚重新编译后,使用“WITH RECOMPILE”的存储过程运行得更快?
- javascript - 如何从函数返回错误?
- python - 在 Python 中抓取 onclick 表
- java - 是否可以创建必要/必需的接口?
- json - 如何避免 json 字符串中的额外嵌套
- elasticsearch - 弹性,无法设置三个节点的集群
- node.js - 如何在 VS Code 中打开 Node.JS 终端
- python - 如何在 Python 中运行 MX 记录查找回退到 A 记录
- java - Spring 注入 SessionFactory 或 EntityManagerFactory 而不是 EntityManager
- python - 将 keras 模型输入馈送到输出层