You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Oracle工作日计算函数number_of_days周日入参返回小数优化咨询

Oracle工作日计算函数优化方案

问题根因

非整数结果出现的核心原因是第二步完整周计算逻辑未做边界校验:当起始日期为周日时,计算得到的完整周区间可能出现不足1周的无效时长,直接执行/7*5运算就会得到小数结果,使用ROUND()属于临时补丁,容易引发其他边界场景的计算误差。

优化方案

方案1:最小改动修复原有逻辑

仅修改第二步的计算逻辑,增加完整周有效性判断即可从根源解决问题,无需调整其他逻辑:

CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN  NUMBER
IS  v_number_of_days NUMBER;
    first_week_day DATE := TO_DATE('31-12-2017', 'DD-MM-YYYY');
    v_full_start DATE;
    v_full_end DATE;
    v_full_weeks NUMBER;
BEGIN
-- 先计算完整周的起止边界
SELECT 
    CASE WHEN MOD(start_date - first_week_day, 7) > 1
        THEN start_date + 8 - MOD(start_date - first_week_day, 7)
        ELSE start_date END,
    CASE WHEN MOD(end_date - first_week_day, 7) < 7
        THEN end_date - MOD(end_date - first_week_day, 7)
        ELSE end_date END
INTO v_full_start, v_full_end
FROM DUAL;
-- 计算有效完整周数,若起始大于结束则为0
v_full_weeks := CASE WHEN v_full_start > v_full_end THEN 0 ELSE FLOOR((v_full_end - v_full_start +1)/7) END;

SELECT 
--step 1 统计首周剩余工作日
( CASE  WHEN MOD(start_date - first_week_day, 7) BETWEEN 2 AND 5
        THEN 6 - MOD(start_date - first_week_day, 7)
        ELSE 0  END )
+
--step 2 有效完整周直接乘每周5个工作日
v_full_weeks *5
+
--step3 统计尾周剩余工作日
( CASE  WHEN MOD(end_date - first_week_day, 7) BETWEEN 1 AND 6
        THEN CASE WHEN MOD(end_date - first_week_day, 7) = 6
            THEN MOD(end_date - first_week_day, 7) - 1
            ELSE MOD(end_date - first_week_day, 7) END
        ELSE 0 END  )
INTO    v_number_of_days
FROM    DUAL;
RETURN  v_number_of_days;
END;
/

方案2:更易维护的简洁实现

如果你的日期区间跨度不会超过几年,用生成日期序列再过滤的方案逻辑更清晰,基本不会出现边界bug:

CREATE OR REPLACE FUNCTION number_of_days(start_date IN DATE, end_date IN DATE)
RETURN NUMBER
IS
    v_work_days NUMBER;
BEGIN
    SELECT COUNT(*)
    INTO v_work_days
    FROM (
        SELECT start_date + LEVEL -1 AS dt
        FROM DUAL
        CONNECT BY LEVEL <= end_date - start_date +1
    )
    -- 排除周六周日:MOD(dt - TO_DATE('31-12-2017','DD-MM-YYYY'),7) 0为周日,6为周六
    WHERE MOD(dt - TO_DATE('31-12-2017','DD-MM-YYYY'),7) NOT IN (0,6);
    RETURN v_work_days;
END;
/

测试验证

测试你给出的示例入参(2021-11-21为周日,2021-11-24为周三),两个方案均返回正确结果3。

内容的提问来源于stack exchange,提问作者adict

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 22:54:06