Oracle
 sql >> Database >  >> RDS >> Oracle

Confronto data Oracle interrotto a causa dell'ora legale

Per evitare questo errore, considera l'utilizzo di un cast esplicito dell'espressione nella clausola where in un tipo di timestamp (timestamp senza fuso orario), in questo modo:

select * 
from MY_TABLE T
where T.MY_TIMESTAMP >= cast(CURRENT_TIMESTAMP - interval '1' hour As timestamp );

In alternativa, puoi impostare esplicitamente il fuso orario della sessione, ad esempio '-05:00' - per l'ora standard (invernale) di New York,
utilizzando ALTER SESSION time_zone = '-05:00' o impostando la variabile di ambiente ORA_SDTZ in tutti gli ambienti del client,
vedere questo collegamento per i dettagli:http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

Ma dipende anche da cosa realmente è memorizzato nella colonna timestamp della tabella, ad esempio quale timestamp 2014-07-01 15:00:00 rappresenta infatti, è un "orario invernale" o un "orario estivo"?

CURRENT_TIMESTAMP la funzione restituisce un valore di tipo di dati TIMESTAMP CON FUSO ORARIO
vedi questo link:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm

Mentre confrontando timestamp e date, Oracle converte implicitamente i dati nel tipo di dati più preciso utilizzando il fuso orario della sessione!
Vedi questo link --> http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251

Nel nostro caso particolare, Oracle esegue il cast timestamp colonna al timestamp with time zone type.

Oracle determina un fuso orario della sessione dall'ambiente client.
Puoi determinare il fuso orario della sessione corrente utilizzando questa query:

select sessiontimezone from dual;

Ad esempio sul mio PC (Win 7), quando è selezionata l'opzione ""Regola automaticamente l'orologio per l'ora legale", questa query restituisce (sotto SQLDeveloper):

SESSIONTIMEZONE                                                           
---------------
Europe/Belgrade 


Quando deseleziono questa opzione in Windows e quindi riavvio SQLDeveloper, viene visualizzato:

SESSIONTIMEZONE                                                           
---------------
+01:00     

Il fuso orario della sessione precedente è un fuso orario con un nome di regione, per il quale Oracle utilizza le regole dell'ora legale per questa regione nei calcoli della data:

alter session set time_zone = 'Europe/Belgrade';
select cast( timestamp '2014-01-29 01:30:00' as timestamp with time zone ) As x,
       cast( timestamp '2014-05-29 01:30:00' as timestamp with time zone ) As y
from dual;

session SET altered.
X                            Y                          
---------------------------- ----------------------------
2014-01-29 01:30:00 EUROPE/B 2014-05-29 01:30:00 EUROPE/B 
ELGRADE                      ELGRADE       


L'ultimo fuso orario utilizza un offset fisso "+01:00" (sempre "l'ora invernale") e Oracle non applica alcuna regola per l'ora legale, ma aggiunge semplicemente l'offset fisso.

alter session set time_zone = '+01:00';
select cast( timestamp '2014-01-29 01:30:00' as timestamp with time zone ) As x,
       cast( timestamp '2014-05-29 01:30:00' as timestamp with time zone ) As y
from dual;

session SET altered.
X                            Y                          
---------------------------- ----------------------------
2014-01-29 01:30:00 +01:00   2014-05-29 01:30:00 +01:00  

Si prega di notare, per curiosità, che Y i risultati di cui sopra rappresentano due tempi diversi !!!
014-05-29 01:30:00 EUROPE/BELGRADE non è uguale a:2014-05-29 01:30:00 +01:00

ma in realtà questo:
014-05-29 01:30:00 EUROPE/BELGRADE è uguale a:2014-05-29 01:30:00 +02:00

Quanto sopra serve solo per renderti consapevole di come il semplice "deselezionare la casella" potrebbe influenzare le tue query e dove scavare per un motivo quando gli utenti si lamentano "questa query ha funzionato bene a gennaio, ma ha fornito risultati sbagliati a luglio".

E sempre sull'argomento ORA-01878 - diciamo che la mia sessione è EUROPE/Warsaw e la mia tabella contiene questo timestamp (senza fuso orario)

'TIMESTAMP'2014-03-30 2:30:00'

Si noti che nella mia regione il cambio dell'ora legale, nell'anno 2014, avviene il 30 marzo alle 2:00 del mattino.
Significa semplicemente che il 30 marzo, alle 2:00 di notte, devo svegliarmi e cambiare l'orologio avanti dalle 2:00 alle 3:00;)

alter session set time_zone = 'Europe/Warsaw';
select cast( TIMESTAMP'2014-03-30 2:30:00' as timestamp with time zone ) As x
from dual;

SQL Error: ORA-01878: podane pole nie zostało znalezione w dacie-godzinie ani w interwale
01878. 00000 -  "specified field not found in datetime or interval"
*Cause:    The specified field was not found in the datetime or interval.
*Action:   Make sure that the specified field is in the datetime or interval.

Oracle sa che questo timestamp non è valido nella mia regione secondo le regole dell'ora legale, perché il 30 marzo non c'è l'ora 2:30 - alle 2:00 l'orologio viene spostato alle 3:00 e non c'è l'ora 2:30. Pertanto Oracle genera l'errore ORA-01878.

Tuttavia questa query funziona perfettamente:

alter session set time_zone = '+01:00';
select cast( TIMESTAMP'2014-03-30 2:30:00' as timestamp with time zone ) As x
from dual;

session SET altered.
X                          
----------------------------
2014-03-30 02:30:00 +01:00 

E questo è il motivo di questo errore:la tua tabella contiene timestamp come 2014-03-09 2:30 o giù di lì (per New York, dove i cambiamenti dell'ora legale si verificano il 9 marzo e il 2 novembre) e Oracle non sa come convertirli da timestamp (senza TZ) a timestamp con TZ.

L'ultima domanda:perché la query con >= non funziona, ma la query con <= funziona bene?

Funzionano/non funzionano, perché SQLDeveloper restituisce solo le prime 50 righe (forse 100? Dipende dalle impostazioni). La query non legge l'intera tabella, si interrompe quando vengono recuperate le prime 50(100) righe.
Modifica la query "funzionante", ad esempio:

select sum( EXTRACT(HOUR from MY_TIMESTAMP) ) from MY_TABLE 
where MY_TIMESTAMP <= (CURRENT_TIMESTAMP - interval '1' hour );

Questo forza la query a leggere tutte le righe della tabella e apparirà l'errore, ne sono sicuro al 100%.