CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT COUNT ( * ) INTO l_no_of_days
FROM (SELECT date_extraction, TO_CHAR (date_extraction, 'DAY')
FROM (SELECT TO_DATE(p_date_start,'DD-MON-RRRR')
+ LEVEL - 1 date_extraction FROM DUAL CONNECT BY LEVEL <
(TO_DATE (p_date_end, 'DD-MON-RRRR')- TO_DATE (p_date_start,'DD-MON-RRRR'))+ 2)
WHERE TRIM (TO_CHAR (date_extraction, 'DAY')) NOT IN ('SATURDAY', 'SUNDAY'));
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;
答案 0 :(得分:1)
正如您在评论中提到的,该功能的关键是分层子查询:
SELECT TO_DATE(p_date_start,'DD-MON-RRRR') + LEVEL - 1 date_extraction
FROM DUAL
CONNECT BY LEVEL <
(TO_DATE(p_date_end,'DD-MON-RRRR')-
TO_DATE(p_date_start,'DD-MON-RRRR'))+ 2
分层查询尝试遍历树(CONNECT BY子句指定父项和子项的关联方式)。在这个例子中,我们发现了一个棘手的使用(或滥用)连接的方法。
此子查询生成从p_date_start到p_date_end(包括两者)的日期。怎么做?
请注意,在CONNECT BY中与LEVEL进行比较的表达式是一个常量,它是结束日期之后的开始日期和结束日期之后的天数(为什么结束日期之后的第二天?因为它是使用&lt;和结束日期之后的第二天是间隔的第一天):
(TO_DATE(p_date_end,&#39; DD-MON-RRRR&#39;) - TO_DATE(p_date_start,&#39; DD-MON-RRRR&#39;))+ 2
select获取DUAL行(它只有一行)此行具有LEVEL 1(分层查询使用伪列LEVEL来指示它开始评估的根的深度)。
评估表达式:
TO_DATE(p_date_start,&#39; DD-MON-RRRR&#39;)+ LEVEL - 1
这是开始日期加上级别减1:这是开始日期。
函数中的外部查询仅过滤SATURDAYS和SUNDAYS并计算剩余天数。
虽然oracle非常有效地评估此查询,但此函数使用强力解决方案。
可以使用更优雅的数学解决方案(无需迭代)。我们有一个等式计算两个日期之间特定日期的数量:
TRUNC(( END – START – DAYOFWEEK(END-DAYOFWEEKTOBECOUNTED) + 8) / 7)
其中DAYOFWEEK是一个返回0-6的函数(0星期日,1星期一... 6星期六)。 DAYOFWEEKTOBECOUNTED是以相同格式计算的当天数。
请注意,TO_CHAR(日期,&#39; d&#39;)以1..7格式返回星期几我们必须纠正为0..6格式(在我的区域星期一是星期的第一天,所以我得到星期日为0和星期六为6与mod函数如下):
MOD(TO_NUMBER(TO_CHAR(p_date_end, 'd')), 7)
最后,我们希望区间内的天数减去星期日(第0天)和星期六(第6天)的天数。因此,采用数学方法的最终程序将是:
CREATE OR REPLACE FUNCTION exlude_weekends (p_date_start DATE,
p_date_end DATE)
RETURN NUMBER
AS
l_no_of_days NUMBER := NULL;
BEGIN
SELECT TRUNC(p_date_end - p_date_start) + 1 -
( TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 0, 'd')), 7)+8)/7)
+ TRUNC((p_date_end - p_date_start -
MOD(to_number(to_char(p_date_end - 6, 'd')), 7)+8)/7)
)
INTO l_no_of_days
FROM DUAL;
RETURN l_no_of_days;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END exlude_weekends;