【问题标题】:Oracle upcoming birthdate todays year甲骨文今年即将迎来生日
【发布时间】:2019-10-02 17:30:55
【问题描述】:

我想写一个查询,告诉我一个人今年的生日是什么时候。对于闰年,下个月就可以了。我打算通过计算今天和他们的出生日期之间的差异并将其添加到他们的出生日期来实现这一点。如果一个人出生于 1999 年 11 月 1 日,我想计算他们今年的生日是什么时候。预计产量是今年 11 月 1 日。这是我实现这一目标的尝试:

我尝试计算两个日期之间的月数并将其添加到出生日期。

select  ADD_MONTHS(to_date('01-NOV-1999'), ROUND(MONTHS_BETWEEN(TRUNC(SYSDATE), to_date('01-NOV-1999'))))  from dual;

这会输出以下内容: 01-OCT-19

我尝试计算出生日期和今天之间的天数,并将其添加到他们的出生日期。但是,这似乎无法正常工作:

select  to_date('01-NOV-1999') + (TRUNC(SYSDATE) - to_date('01-NOV-1999'))  from dual;

输出:02-OCT-19

我希望得到的是 2019 年 11 月 1 日(我发布的系统日期是 19 年 10 月 2 日),这似乎都比我的预期输出晚了一个月。如果我尝试在第一个查询中使用 OCT 而不是 NOV 运行相同的查询,它可以正常工作。

我正在使用 Oracle 数据库 11g

【问题讨论】:

    标签: sql oracle oracle11g


    【解决方案1】:

    生日不是从出生日期算起的公式。这是一年中的固定日期,闰年除外。使用该逻辑,您可以使用以下查询计算今年的生日:

    with date_of_birth as
    (SELECT TO_DATE('01-NOV-1971','DD-MON-YYYY') as dt FROM DUAL
    UNION
    SELECT TO_DATE('29-FEB-2004','DD-MON-YYYY')FROM DUAL
    )
    SELECT 
      dt as date_of_birth
     ,CASE 
        WHEN TO_CHAR(dt,'DD-MON') = '29-FEB' THEN
          TO_DATE('01-MAR-'||EXTRACT(year FROM sysdate),'DD-MON-YYYY')
        ELSE 
          TO_DATE(TO_CHAR(dt,'DD-MON-')||EXTRACT(year FROM sysdate),'DD-MON-YYYY')
      END AS this_year_birthday
      FROM date_of_birth;
    

    它会返回

    DATE_OF_BIR THIS_YEAR_B
    ----------- -----------
    01-NOV-1971 01-NOV-2019
    29-FEB-2004 01-MAR-2019
    

    --已编辑。正如 Jeffrey 指出的,如果在闰年运行,​​上述查询会返回错误的结果。这是一个更新版本,允许用户在今年以外的时间运行它。将返回与 Jeffrey Kemps 的答案相同的结果,但从另一个角度来看。

    with 
    FUNCTION is_valid_date (date_str_i VARCHAR2, format_i VARCHAR2) RETURN VARCHAR2
    /* check if date is valid */
    AS
      l_dummy_dt DATE;
      date_not_valid_for_m EXCEPTION;
      PRAGMA EXCEPTION_INIT(date_not_valid_for_m, -01839);  
    BEGIN
      SELECT TO_DATE(date_str_i,format_i) INTO l_dummy_dt FROM DUAL;
      RETURN 'Y';
    EXCEPTION WHEN date_not_valid_for_m THEN
      RETURN 'N';
    END; 
    date_of_birth as
    (SELECT TO_DATE('01-NOV-1971','DD-MON-YYYY') as dt FROM DUAL
    UNION
    SELECT TO_DATE('29-FEB-2004','DD-MON-YYYY')FROM DUAL
    ),
    year_to_check as
    (SELECT '2016' as yr FROM DUAL)
    SELECT 
      dt as date_of_birth
     ,CASE 
        WHEN TO_CHAR(dt,'DD-MON') = '29-FEB' THEN
          CASE 
            WHEN is_valid_date(date_str_i => '29-FEB-'||yr,format_i => 'DD-MON-YYYY') = 'N' 
              THEN TO_DATE('01-MAR-'||yr,'DD-MON-YYYY')
            ELSE
              TO_DATE(TO_CHAR(dt,'DD-MON-')||yr,'DD-MON-YYYY')
            END 
        ELSE 
          TO_DATE(TO_CHAR(dt,'DD-MON-')||yr,'DD-MON-YYYY')
      END AS this_year_birthday
      FROM date_of_birth, year_to_check;
    /
    

    【讨论】:

    • 这对于 2 月 29 日出生的人来说会产生错误的日​​期,在闰年他们会(正确地)期望他们的生日庆祝活动在那些年的 2 月 29 日举行。对于这些情况,您的查询返回 3 月 1 日。
    • 感谢您指出这一点!我添加了第二个版本,用于检查 29-FEB 是否为有效日期,如果不是,则仅将其更改为 3 月 1 日。
    【解决方案2】:

    这是令人惊讶的复杂,它激发了整个博客文章的灵感。这是一个应该满足大多数人的解决方案:

    with
      function birthday (d in date, age in number) return date is
      begin
        if to_char(d,'DD/MM') = '28/02'
        and to_char(add_months(d,age*12),'DD/MM') = '29/02'
        then
          return add_months(d,age*12)-1;
        else
          return add_months(d,age*12);
        end if;
      end;
    select * from (
      with testdata as (
      select date'2000-12-25' as d1
            ,date'2000-02-29' as d2
            ,date'2000-02-28' as d3
            ,date'2001-02-28' as d4
      from dual)
      select rownum-1 as age
            ,birthday(d1,rownum-1) as d1
            ,birthday(d2,rownum-1) as d2
            ,birthday(d3,rownum-1) as d3
            ,birthday(d4,rownum-1) as d4
      from testdata connect by level <= 12
    );
    

    https://jeffkemponoracle.com/2018/09/many-happy-birthdays/

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-06
    • 1970-01-01
    • 2011-01-03
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多