1

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

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)正在破坏聚会。

4

2 回答 2

2

4th of Febto4th 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)*/

小提琴演示

于 2014-10-05T16:43:24.547 回答
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
于 2015-07-10T02:31:56.440 回答