Oracle中的MONTHS_BETWEEN。精确的计算逻辑

时间:2014-10-05 16:11:51

标签: sql oracle sql-date-functions

Oracle函数之间的确切逻辑是什么?
我试过这个查询:

SELECT D1, 
       D2, 
       MONTHS_BETWEEN (D1, D2) DIFF,
       (D1-D2)/31 MANUAL_CALC1,
       (D1-D2)/30 MANUAL_CALC2,
       (D1-D2)/29 MANUAL_CALC3,
       (D1-D2)/28 MANUAL_CALC4
FROM (SELECT TO_DATE('07-03-2014', 'DD-MM-YYYY') D1, 
             TO_DATE('04-02-2014', 'DD-MM-YYYY') D2
        FROM DUAL);

结果如下:
DIFF:1.09
MANUAL_CALC1:1
MANUAL_CALC2:1.03
MANUAL_CALC3:1.06
MANUAL_CALC4:1.10

我正在将Oracle包转换为Java程序,我需要生成完全相同的结果。但只是这一个功能(months_between)正在破坏党。

2 个答案:

答案 0 :(得分:2)

4th of Feb4th of March1月..

剩余 3 天到7th of March ..所以3/31(总是31作为基准)= .09个月

所以答案总是1+ 0.09 = 1.09

公式如此,

FLOOR(ABS(MONTHS_BETWEEN(D1,D2))) + /* The Actual Month difference, as whole number */
 (TO_CHAR(D1,'DD') - TO_CHAR(D2,'DD'))/31 /* Remaining Days / 31)*/

Fiddle Demo

答案 1 :(得分:0)

SELECT
    a.dt_base,
    a.dt,
    a.months,
    a.days,
    a.mms,
    a.my_months,
    a.bias,
    a.day_part,
    a.my_add_months
FROM
    (
        SELECT
            a.dt_base,
            a.dt,
            a.months,
            a.days,
            a.mms,
            a.my_months,
            31 - TO_NUMBER(TO_CHAR(LAST_DAY(ADD_MONTHS(a.dt_base,TRUNC(a.months))),'dd')) bias,
            ((a.months - TRUNC(a.months)) * 31) day_part,
            CASE WHEN TO_CHAR(ADD_MONTHS(a.dt_base,TRUNC(a.months)),'yyyymm') != TO_CHAR((ADD_MONTHS(a.dt_base,TRUNC(a.months)) + ((a.months - TRUNC(a.months)) * 31)),'yyyymm') THEN
                ADD_MONTHS(a.dt_base,TRUNC(a.months)) + ((a.months - TRUNC(a.months)) * 31) - (31 - TO_NUMBER(TO_CHAR(LAST_DAY(ADD_MONTHS(a.dt_base,TRUNC(a.months))),'dd')))
            ELSE 
                ADD_MONTHS(a.dt_base,TRUNC(a.months)) + ((a.months - TRUNC(a.months)) * 31)
            END my_add_months
        FROM
            (
                SELECT
                    a.dt_base,
                    a.dt,
                    a.months,
                    a.days,
                    a.mms,
                    mms + days / 31 my_months
                FROM
                    (
                        SELECT
                            a.dt_base,
                            a.dt,
                            a.months,
                            CASE WHEN TO_CHAR(a.dt_base,'dd') > to_char(a.dt,'dd') THEN
                                31 - TO_NUMBER(TO_CHAR(a.dt_base,'dd')) + TO_NUMBER(TO_CHAR(a.dt,'dd'))
                            ELSE
                                TO_NUMBER(TO_CHAR(a.dt,'dd')) - TO_NUMBER(TO_CHAR(a.dt_base,'dd'))
                            END days
                            , CASE WHEN TO_CHAR(a.dt_base,'dd') > TO_CHAR(a.dt,'dd') THEN
                                ( TO_NUMBER(TO_CHAR(a.dt,'yyyy')) * 12 + TO_NUMBER(TO_CHAR(a.dt,'mm')) )
                                -
                                ( TO_NUMBER(TO_CHAR(a.dt_base,'yyyy')) * 12 + TO_NUMBER(TO_CHAR(a.dt_base,'mm')) )
                                - 1
                            ELSE
                                ( TO_NUMBER(TO_CHAR(a.dt,'yyyy')) * 12 + TO_NUMBER(TO_CHAR(a.dt,'mm')) )
                                -
                                ( TO_NUMBER(TO_CHAR(a.dt_base,'yyyy')) * 12 + TO_NUMBER(TO_CHAR(a.dt_base,'mm')) )
                            END mms
                        FROM
                            (
                                SELECT
                                    dt_base,
                                    dt,
                                    MONTHS_BETWEEN(dt,dt_base) months
                                FROM
                                    (
                                        SELECT
                                            TRUNC(sysdate,'dd') dt_base,
                                            TRUNC(sysdate,'dd') + level - 1 dt
                                        FROM    dual
                                        CONNECT BY level < 36500
                                    ) a
                            ) a
                    )a
            ) a
    ) a
WHERE   a.dt != a.my_add_months