Domanda

Visualizza i risultati di seguito query:

>> 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

Come posso ottenere '30 -MAR-11' , quando l'aggiunta di 4 mesi in una certa data?

Si prega di aiuto.

È stato utile?

Soluzione

C'è un'altra domanda qui su Oracle e Java

Si afferma che

Dal di riferimento Oracle su add_months http : //download-west.oracle.com/docs/cd/B19306_01/server.102/b14200/functions004.htm

Se la data è l'ultimo giorno del mese o se il mese risultante ha meno giorni rispetto alla componente giorno della data, quindi il risultato è l'ultimo giorno del mese risultante. In caso contrario, il risultato ha lo stesso componente di giorno come data.

Quindi immagino che devi controllare manualmente indicando giorno e termina giorno per cambiare il comportamento della funzione. O forse con l'aggiunta di giorni invece di mesi. (Ma non ho trovato una funzione add_day nella ref)

Altri suggerimenti

Per risolvere il problema, mi potrebbe eventualmente utilizzare questo algoritmo:

  1. Calcola la data obiettivo TargetDate1 utilizzando ADD_MONTHS.
  2. In alternativa calcolare la data di destinazione TargetDate2 in questo modo:

    1) si applicano ADD_MONTHS alla prima del mese della data di fonte;
    2) aggiungere la differenza di giorni tra la fonte e l'inizio dello stesso mese.

  3. Selezionare il LEAST tra il TargetDate1 e TargetDate2.

Così, alla fine, la data obiettivo conterrà una componente diversi giorni se componente giorno della data di origine è maggiore del numero del giorno nel mese di destinazione. In questo caso la data obiettivo sarà l'ultimo giorno del mese corrispondente.

Non sono davvero sicuro di mia conoscenza della sintassi SQL di Oracle, ma in fondo l'implementazione potrebbe essere simile a questo:

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
)

Ecco un'illustrazione dettagliata di come funziona il metodo:

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'

E qui ci sono alcuni più esempi per dimostrare casi diversi:

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

È possibile utilizzare intervallo di aritmetica per ottenere il risultato che si desidera

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

Essere consapevoli, tuttavia, che ci sono delle trappole per intervallo di aritmetica, se si sta lavorando con i giorni che non esistono in ogni mese

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

Che ne dite di qualcosa di simile a questo:

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
;

Risultato: 30-NOV-10

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
;

Risultato: 30-MAR-11

la funzione add_months restituisce una data più n mesi.

Dal 30 novembre è l'ultimo giorno del mese, l'aggiunta di 4 mesi si tradurrà in una data che è la fine di 4 mesi. Questo è il comportamento previsto. Se le date non sono destinate a cambiare, una soluzione è quello di sottrarre un giorno dopo la nuova data è stato restituito

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

ADD_MONTH
---------
30-MAR-11
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;

Questa sezione in paranthesis:

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

ti dà il numero di giorni tra la data di immesso e 4 mesi più tardi. Quindi, aggiungendo il numero di giorni fino alla data dato dà la data esatta dopo 4 mesi.

Rif: http://www.dba-oracle.com/t_test_data_date_generation_sql.htm

Soluzione semplice:

ADD_MONTHS(date - 1, x) + 1

Ecco il trucco:

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

Enjoy!

    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;        

Query-risultato

STARTDATE 2014/2/28

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top