最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
SQL存储过程中如何计算两个日期点之间的精准工作时长
时间:2026-07-03 11:05:47 编辑:袖梨 来源:一聚教程网
必须建节假日表并用CTE生成日期范围,结合每日工作时段交集计算有效工时,显式设置DATEFIRST避免周末误判。
不能只用 DATEDIFF 算总小时再扣周末——它不认节假日、不切工作时段、不处理跨天交集,结果必然偏高。
必须建节假日表,且字段类型得是 DATE
别用 DATETIME 存假期,否则 JOIN 时因时分秒不匹配导致漏判。表结构至少要包含:
-
holiday_date DATE—— 主键,精确到日 -
is_workday BIT——0表示放假,1表示调休上班(如周日补班)
索引加在 holiday_date 上,不然大范围查询时 LEFT JOIN 会慢成瓶颈。硬编码春节/国庆日期?每年改 SQL 脚本,上线前还得人工核对,没人敢碰。
用 CTE 批量生成日期,别写 WHILE 循环
常见错误:在标量函数里写 WHILE,每天查一次 EXISTS(SELECT 1 FROM dbo.Holidays)。跨度三年就是上千次单行查询,CPU 拉满、超时频发。
正确做法是用递归 CTE 或数字表一次性生成所有中间日期:
WITH dates AS ( SELECT DATEADD(DAY, n.n, CAST(@start_datetime AS DATE)) AS dt FROM dbo.Numbers n WHERE n.n BETWEEN 0 AND DATEDIFF(DAY, @start_datetime, @end_datetime))
没 Numbers 表?开发环境可临时用 master..spt_values 顶替,但生产环境必须建真实表——它只是个带整数列的辅助表,建一次用十年。
每天单独算交集,再 SUM 有效分钟
工作时段是 08:00–20:00,就得对每一天分别截取和输入时间的交集:
work_start = CAST(dt AS DATETIME) + '08:00'work_end = CAST(dt AS DATETIME) + '20:00'actual_start = CASE WHEN @start_datetime > work_start THEN @start_datetime ELSE work_start ENDactual_end = CASE WHEN @end_datetime < work_end THEN @end_datetime ELSE work_end END
最后过滤掉 actual_start >= actual_end 的无效段,再 SUM(DATEDIFF(MINUTE, actual_start, actual_end)) / 60.0 得到小时数。直接 DATEDIFF(HOUR, @start, @end) 会把凌晨三点也算进去。
DATEPART(WEEKDAY, ...) 结果依赖 SET DATEFIRST
默认 DATEFIRST = 7(周日=1),但某些连接被设成 DATEFIRST = 1(周一=1),这时周六返回 6 还是 7 不确定。写死 NOT IN (1,7) 在部分环境里会把周六当工作日。
稳妥做法是在逻辑开头显式重置:
SET DATEFIRST 1;
后续用 DATEPART(WEEKDAY, d.dt) NOT IN (6,7) 判周末。别指望靠 (DATEPART(...) + @@DATEFIRST - 2) % 7 这种绕过方案——调试时根本看不出哪天被误判了。
真正麻烦的不是写逻辑,而是维护:节假日表要年年更新,调休日要人工录入,CTE 生成的日期范围一旦超出现有表数据,结果就静默变少。上线前务必用含调休、跨年、含周末的边界案例跑一遍验证。
相关文章
- 刀剑缭乱2026公测兑换码大全一览 07-05
- 崩坏星穹铁道4.0卡池7个新角色一览 07-05
- 明日方舟终末地开服工业蓝图一览 工业蓝图作用与使用思路解析 07-05
- 原神梦之树怎么开启 梦之树开启条件 07-05
- 帕瓦勇者传说持续伤害阵容搭配推荐 07-05
- 明日方舟:终末地全新玩法 蚀像寻遗怎么玩介绍 07-05