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

时间:2015-04-21 15:46:53

标签: sql oracle oracle11g date-arithmetic

我正在使用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将两者都转换为日期。< / p>

 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;

但是,如果我更改此查询以使用sysdate + 9char转换为to_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;

3 个答案:

答案 0 :(得分:3)

不幸的是,你的逻辑并不完全准确。 以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

这恰好是耶稣受难日...所以“不是营业日”......它应该吐出“2013年3月28日”

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

为了做这种事情,你真的需要一张假期表(或工作日表 - 无论哪种方式) 一旦你有了,解决方案变得微不足道了:

如果您有假期表:

  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.

如果您有工作日表:

  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.

答案 1 :(得分:1)

这是使用您的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设置,并且很容易这么做,因为有很多明确和隐含的转换正在进行。

答案 2 :(得分:1)

我的建议是使用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)提供日期时,此功能将返回下个月的最后一个工作日(即2015-06-26)。

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

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

在公共假日期间,请看看其中一个:

Due Date Calculated Given Start Date and Working/Lunch Hours PL/SQL

Sql: difference between two dates