Oracle 中的日期比较以查找今天是否是本月的最后一个工作日

Date Comparison in Oracle to find whether today is Last Business Day of Month

我正在使用 Oracle 11g。

所以,我有这个查询,它为我提供了 Last Business Day of a Month任何关于更好查询的建议总是受欢迎的

select DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
              to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
              to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
              to_char(last_day(sysdate), 'DD-MON-YYYY'))
  into LAST_BD_OF_MONTH_P
  from dual;

今天的结果是这样的

30-APR-2015

现在,当我将它与 sysdate + 9 进行比较以检查它是否为月末时,它总是给我一个 No Match - 看到我正在使用 to_date 将两者转换为日期.

 select to_char(sysdate + 9,'DD-MON-YYYY')
        , DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
          to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
          to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
          to_char(last_day(sysdate), 'DD-MON-YYYY')) as EOMBD
        , case when to_date(sysdate + 9,'DD-MON-YYYY') = 
                    to_date(DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
                    to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
                    to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
                    to_char(last_day(sysdate), 'DD-MON-YYYY')), 'DD-MON-YYYY') 
               then 'Match' 
               else 'No Match' 
          end as Match
    from dual;

但是,如果我更改此查询以使用 to_charsysdate + 9 转换为 char,它会起作用并为我提供 Match.

 select to_char(sysdate + 9,'DD-MON-YYYY')
        , DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
          to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
          to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
          to_char(last_day(sysdate), 'DD-MON-YYYY')) as EOMBD
        , case when /*convert using to_char*/to_char(sysdate + 9,'DD-MON-YYYY') = 
                    DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
                    to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
                    to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
                    to_char(last_day(sysdate), 'DD-MON-YYYY')) 
               then 'Match' 
               else 'No Match' 
          end as Match
    from dual;

我知道在第二个查询中,它与 strings 匹配,因此给了我一个 Match。有什么办法吗,这种比较为我提供了 Match 的结果,而无需将其转换为 char?

这确实给了我想要的输出,但我不想在这里使用 to_char 函数。

PS : LAST_BD_OF_MONTH_P 声明为 DATE

包含答案

 select to_char(sysdate + 9,'DD-MON-YYYY'),  DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
      to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
      to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
      to_char(last_day(sysdate), 'DD-MON-YYYY')) as EOMBD
    , case when trunc(sysdate + 9) = 
                to_date(DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
                to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
                to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
                to_char(last_day(sysdate), 'DD-MON-YYYY')), 'DD-MON-YYYY') 
           then 'Match' 
           else 'No Match' 
      end as Match
from dual;

这是使用您的 NLS_DATE_FORMAT 将 sysdate + 9 隐式转换为字符串;然后转换回日期:

case when to_date(sysdate + 9,'DD-MON-YYYY') = 

如果您的格式也是 DD-MON-YYYY,这充其量也行得通,但实际上您正在失去世纪,因为您确实在做:

case when to_date(to_char(sysdate + 9, 'DD-MON-RR'),'DD-MON-YYYY') = 

RR 和 YYYY 不匹配意味着您最终得到的是 0015 年,而不是 2015 年。

您可以将其简化为:

case when trunc(sysdate) + 9 = 

因此,只需进行更改:

 select to_char(sysdate + 9,'DD-MON-YYYY')
        , DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
          to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
          to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
          to_char(last_day(sysdate), 'DD-MON-YYYY')) as EOMBD
        , case when trunc(sysdate + 9) = 
                    to_date(DECODE(to_char(last_day(to_date(sysdate)), 'D'),'7',
                    to_char((last_day(sysdate) - 1), 'DD-MON-YYYY'),'1',
                    to_char((last_day(sysdate) - 2), 'DD-MON-YYYY'),
                    to_char(last_day(sysdate), 'DD-MON-YYYY')), 'DD-MON-YYYY') 
               then 'Match' 
               else 'No Match' 
          end as Match
    from dual;

TO_CHAR(SYSDATE+9,'DD-MON-YYYY') EOMBD                MATCH  
-------------------------------- -------------------- --------
30-APR-2015                      30-APR-2015          Match   

我不确定为什么你在其他地方如此频繁地在日期和字符串之间进行转换,你让事情变得复杂并让 NLS 问题成为一个问题。您永远不应该依赖您的 NLS 设置,并且在进行如此多的显式和隐式转换时很容易不小心这样做。

不幸的是,您的逻辑并不完全准确。 以2013年3月为例:

  with w_date as ( select to_date('15-mar-2013','dd-mon-yyyy') d from dual )
  select DECODE(to_char(last_day(d), 'D'),'7',
                to_char((last_day(d) - 1), 'DD-MON-YYYY'),'1',
                to_char((last_day(d) - 2), 'DD-MON-YYYY'),
                to_char(last_day(d), 'DD-MON-YYYY'))
    from w_date;

  DECODE(TO_C
  -----------
  29-MAR-2013

恰好是耶稣受难日..所以"not a business Day" ...它应该吐出来"Mar 28, 2013"

不要做太多 to_date / to_char 转换..这会让你伤心。

为了做这种事情,您确实需要 table 个假期(或 table 个工作日 - 无论哪种方式) 一旦有了它,解决方案就变得微不足道了:

如果您有 table 个假期:

  with w_date as ( select to_date('15-mar-2013','dd-mon-yyyy') d from dual ),
       w_holidays as (
       select to_date('29-mar-2013','dd-mon-yyyy') holiday from dual
          ),
      w_sub as (
           select last_day(d) - level + 1  dd
             from w_date
             connect by level <= 10
        )
  select max(dd)
    from w_sub s
    where to_char(dd,'d') not in ( 1,7)
      and not exists ( select * from w_holidays h
                        where h.holiday = s.dd )

  /

结果:

  MAX(DD)
  ---------
  28-MAR-13

  1 row selected.

如果您有 table 个工作日:

  with w_business_days as (
       select to_date('25-mar-2013','dd-mon-yyyy') busday from dual union all
       select to_date('26-mar-2013','dd-mon-yyyy') busday from dual union all
       select to_date('27-mar-2013','dd-mon-yyyy') busday from dual union all
       select to_date('28-mar-2013','dd-mon-yyyy') busday from dual union all
       select to_date('01-apr-2013','dd-mon-yyyy') busday from dual
          )
  select max(busday)
    from w_business_days
   where busday <= last_day(to_date('15-mar-2013','dd-mon-yyyy') )
  /

结果:

  MAX(BUSDA
  ---------
  28-MAR-13

  1 row selected.

我的建议是使用 DBMS_SCHEDULER 日历。您可以创建这样的函数:

CREATE OR REPLACE FUNCTION LAST_BD_OF_MONTH_P(the_day IN TIMESTAMP) RETURN TIMESTAMP AS
    next_run_date TIMESTAMP;
BEGIN
    DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=MONTHLY; BYDAY=-1 FRI', NULL, the_day, next_run_date);
    RETURN next_run_date;
END;

SELECT CAST(LAST_BD_OF_MONTH_P(DATE '2015-05-10') AS DATE) FROM dual;
=====================================
2015-05-29

在此处查看日历语法:Calendaring Syntax

请注意,当您指定最后一个工作日之后的日期(例如 2015-04-30)时,此函数将 return 下个月的最后一个工作日(即 2015-06-26)。

为了避免这种情况,您可以使用

DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=MONTHLY; BYDAY=-1 FRI', NULL, TRUNC(the_day, 'MM'), next_run_date); 代替。

为了也涵盖 public 假期,请查看其中一个: