9

查看以下查询的结果:

>> SELECT ADD_MONTHS(TO_DATE('30-MAR-11','DD-MON-RR'),-4) FROM DUAL;
30-NOV-10


>> SELECT ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR'),4) FROM DUAL;
31-MAR-11

向某个日期添加 4 个月时,如何获得“30-MAR-11”?

请帮忙。

4

10 回答 10

9

这里还有一个关于Oracle 和 Java的问题

它指出

来自 add_months 上的 Oracle 参考http://download-west.oracle.com/docs/cd/B19306_01/server.102/b14200/functions004.htm

如果 date 是该月的最后一天,或者如果结果月份的天数少于 date 的天部分,则结果是结果月份的最后一天。否则,结果与日期具有相同的日期部分。

所以我猜你必须手动检查声明日和结束日来改变函数的行为。或者也许通过添加天而不是月。(但我没有add_day在 ref 中找到函数)

于 2011-03-18T07:46:14.350 回答
5

作为一种解决方法,我可能会使用这个算法:

  1. TargetDate1使用计算目标日期ADD_MONTHS
  2. 或者像这样计算目标日期TargetDate2

    1) 适用ADD_MONTHS于源日期月份的第一天;
    2)添加源日期和同月初之间的天数差。

  3. 选择 和LEAST之间TargetDate1TargetDate2

所以最后,如果源日期的日期部分大于目标月份的天数,则目标日期将包含不同的日期部分。在这种情况下,目标日期将是相应月份的最后一天。

我不太确定我对 Oracle 的 SQL 语法的了解,但基本上实现可能如下所示:

SELECT
  LEAST(
    ADD_MONTHS(SourceDate, Months),
    ADD_MONTHS(TRUNC(SourceDate, 'MONTH'), Months)
      + (SourceDate - TRUNC(SourceDate, 'MONTH'))
  ) AS TargetDate
FROM (
  SELECT
    TO_DATE('30-NOV-10', 'DD-MON-RR') AS SourceDate,
    4 AS Months
  FROM DUAL
)

以下是该方法如何工作的详细说明:

SourceDate = '30-NOV-10'
Months     = 4

TargetDate1 = ADD_MONTHS('30-NOV-10', 4) = '31-MAR-11'  /* unacceptable */
TargetDate2 = ADD_MONTHS('01-NOV-10', 4) + (30 - 1)
            = '01-MAR-11' + 29 = '30-MAR-11'            /* acceptable */
TargetDate  = LEAST('31-MAR-11', '30-MAR-11') = '30-MAR-11'

这里还有一些例子来展示不同的情况:

SourceDate | Months | TargetDate1 | TargetDate2 | TargetDate
-----------+--------+-------------+-------------+-----------
 29-NOV-10 |    4   |   29-MAR-11 |   29-MAR-11 |  29-MAR-11
 30-MAR-11 |   -4   |   30-NOV-10 |   30-NOV-10 |  30-NOV-10
 31-MAR-11 |   -4   |   30-NOV-10 |   01-DEC-10 |  30-NOV-10
 30-NOV-10 |    3   |   28-FEB-11 |   02-MAR-11 |  28-FEB-11
于 2011-03-18T10:01:28.483 回答
3

你可以使用区间算术得到你想要的结果

SQL> select date '2011-03-30' - interval '4' month
  2    from dual;

DATE'2011
---------
30-NOV-10

SQL> ed
Wrote file afiedt.buf

  1  select date '2010-11-30' + interval '4' month
  2*   from dual
SQL> /

DATE'2010
---------
30-MAR-11

但是请注意,如果您正在处理每个月都不存在的日子,则区间算术存在缺陷

SQL> ed
Wrote file afiedt.buf

  1  select date '2011-03-31' + interval '1' month
  2*   from dual
SQL> /
select date '2011-03-31' + interval '1' month
                         *
ERROR at line 1:
ORA-01839: date not valid for month specified
于 2011-04-01T07:16:51.413 回答
1

像这样的东西怎么样:

SELECT
    LEAST(
        ADD_MONTHS(TO_DATE('30-MAR-11','DD-MON-RR'),-4),
        ADD_MONTHS(TO_DATE('30-MAR-11','DD-MON-RR')-1,-4)+1
    )
FROM
    DUAL
;

结果:10 年 11 月 30 日

SELECT
    LEAST(
        ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR'),4),
        ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR')-1,4)+1
    )
FROM
    DUAL
;

结果:11 年 3 月 30 日

于 2013-02-15T16:42:01.237 回答
0

add_months函数返回一个日期加上 n 个月。

由于 11 月 30 日是该月的最后一天,因此添加 4 个月将导致日期为 4 个月的结束日期。这是预期的行为。如果日期不会更改,解决方法是在返回新日期后减去一天

SQL> SELECT ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR'),4) -1 from dual;

ADD_MONTH
---------
30-MAR-11
于 2011-03-18T07:46:29.513 回答
0
SELECT TO_DATE('30-NOV-10','DD-MON-RR') + 
       (
        ADD_MONTHS(TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM'),4) - 
        TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM')
       ) RESULT
  FROM DUAL;

此部分在括号中:

ADD_MONTHS(TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM'),4) - TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM')

为您提供输入日期与 4 个月后之间的天数。因此,将此天数添加到您给出的日期会给出 4 个月后的确切日期。

参考:http ://www.dba-oracle.com/t_test_data_date_generation_sql.htm

于 2011-03-19T15:52:34.243 回答
0

简单的解决方案:

ADD_MONTHS(date - 1, x) + 1
于 2015-08-05T13:23:26.907 回答
0

这是诀窍:

select add_months(to_date('20160228', 'YYYYMMDD')-1, 1)+1 from dual;

享受!

于 2017-03-21T03:30:56.697 回答
0

对于这个问题,我们已经找到了更简单的(在我们的理解中)解决方案——从原始日期和 add_month 结果日期中取最少的天数,如下所示:

TRUNC(ADD_MONTHS(input_date,1),'MM') + LEAST(TO_CHAR(input_date, 'DD'), TO_CHAR(ADD_MONTHS(input_date,1), 'DD')) - 1 

在我们的测试结果下方,这里的其他一些示例并非在每个日期都有效:

WITH DATES as (
    SELECT TO_DATE('2020-01-31', 'YYYY-MM-DD HH24:MI:SS') as input_date,
           '2020-02-29' as expected_date
    FROM dual
    UNION ALL
    SELECT TO_DATE('2020-02-28', 'YYYY-MM-DD HH24:MI:SS'),
           '2020-03-28'
    FROM dual
    UNION ALL
    SELECT TO_DATE('2020-09-30', 'YYYY-MM-DD HH24:MI:SS'),
           '2020-10-30'
    FROM dual
    UNION ALL
    SELECT TO_DATE('2020-09-01', 'YYYY-MM-DD HH24:MI:SS'),
           '2020-10-01'
    FROM dual
    UNION ALL
    SELECT TO_DATE('2019-01-30', 'YYYY-MM-DD HH24:MI:SS'),
           '2019-02-28'
    FROM dual
    UNION ALL
    SELECT TO_DATE('2020-02-29', 'YYYY-MM-DD HH24:MI:SS'),
           '2020-03-29'
    FROM dual
    UNION ALL
    SELECT TO_DATE('2020-09-29', 'YYYY-MM-DD HH24:MI:SS'),
           '2020-10-29'
    FROM dual
    UNION ALL
    SELECT TO_DATE('2020-03-01', 'YYYY-MM-DD HH24:MI:SS'),
            '2020-04-01'
    FROM dual
),
methods as (
SELECT
    input_date,
    expected_date,
    ADD_MONTHS(input_date,1) as standard_way,
    add_months(input_date-1, 1)+1 as wrong_way,
    TO_DATE(LEAST(TO_CHAR(input_date, 'DD'), TO_CHAR(ADD_MONTHS(input_date,1), 'DD')) || '-' || TO_CHAR(ADD_MONTHS(input_date,1), 'MM-YYYY'), 'DD-MM-YYYY') as good_way,
    TRUNC(ADD_MONTHS(input_date,1),'MM') + LEAST(TO_CHAR(input_date, 'DD'), TO_CHAR(ADD_MONTHS(input_date,1), 'DD')) - 1 as better_way
FROM
     DATES
)
SELECT
    input_date,
    expected_date,
    standard_way,
    CASE WHEN TO_CHAR(standard_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as standard_way_ok,
    wrong_way,
    CASE WHEN TO_CHAR(wrong_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as wrong_way_ok,
    good_way,
    CASE WHEN TO_CHAR(good_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as good_way_ok,
    better_way,
   CASE WHEN TO_CHAR(better_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as better_way_ok
FROM
    methods
;
于 2020-09-23T16:09:04.700 回答
-1
    CREATE OR REPLACE FUNCTION My_Add_Month(
      STARTDATE           DATE,
      MONTHS_TO_ADD      NUMBER
    )
        RETURN DATE
    IS
        MY_ADD_MONTH_RESULT DATE;
    BEGIN

        SELECT ORACLES_ADD_MONTH_RESULT + NET_DAYS_TO_ADJUST INTO MY_ADD_MONTH_RESULT FROM
        (
            SELECT T.*,CASE WHEN SUBSTRACT_DAYS > ADD_DAYS THEN ADD_DAYS - SUBSTRACT_DAYS ELSE 0 END AS NET_DAYS_TO_ADJUST FROM
            (
                SELECT T.*,EXTRACT(DAY FROM ORACLES_ADD_MONTH_RESULT) AS SUBSTRACT_DAYS FROM
                (
                    SELECT ADD_MONTHS(STARTDATE,MONTHS_TO_ADD) AS ORACLES_ADD_MONTH_RESULT,EXTRACT(DAY FROM STARTDATE) AS ADD_DAYS FROM DUAL
                )T
            )T
        )T;
        RETURN TRUNC(MY_ADD_MONTH_RESULT);
    END My_Add_Month;
    /

    --test & verification of logic & function both
    SELECT T.*,ORACLES_ADD_MONTH_RESULT + NET_DAYS_TO_ADJUST AS MY_ADD_MONTH_RESULT,
    My_Add_Month(STARTDATE,MONTHS_TO_ADD) MY_ADD_MONTH_FUNCTION_RESULT
    FROM
    (
        SELECT T.*,CASE WHEN SUBSTRACT_DAYS > ADD_DAYS THEN ADD_DAYS - SUBSTRACT_DAYS ELSE 0 END AS NET_DAYS_TO_ADJUST FROM
        (
            SELECT T.*,EXTRACT(DAY FROM ORACLES_ADD_MONTH_RESULT) AS SUBSTRACT_DAYS FROM
            (
                SELECT T.*,ADD_MONTHS(STARTDATE,MONTHS_TO_ADD) AS ORACLES_ADD_MONTH_RESULT,EXTRACT(DAY FROM STARTDATE) AS ADD_DAYS FROM
                (
                    SELECT TO_DATE('28/02/2014','DD/MM/YYYY') AS STARTDATE, 1 AS MONTHS_TO_ADD FROM DUAL
                )T
            )T
        )T
    )T;        

查询结果

开始日期2/28/2014

MONTHS_TO_ADD 1

ORACLES_ADD_MONTH_RESULT 2014 年3 月 31 日

ADD_DAYS 28

SUBSTRACT_DAYS 31

NET_DAYS_TO_ADJUST -3

MY_ADD_MONTH_RESULT 2014 年 3 月28 日

MY_ADD_MONTH_FUNCTION_RESULT 2014 年 3 月 28 日

于 2014-04-30T19:21:28.387 回答