sql >> Database >  >> RDS >> Oracle

Oracle-datumvergelijking verbroken vanwege DST

Om deze fout te voorkomen, kunt u overwegen een expliciete cast van de expressie in de where-component te gebruiken naar een tijdstempeltype (tijdstempel zonder tijdzone), op deze manier:

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

Als alternatief kunt u de sessietijdzone expliciet instellen op bijvoorbeeld '-05:00' - voor de standaard (winter)tijd van New York,
met behulp van ALTER SESSION time_zone = '-05:00' , of door de ORA_SDTZ-omgevingsvariabele in alle clientomgevingen in te stellen,
zie deze link voor details:http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

Maar het hangt ook af van wat echt wordt opgeslagen in de tijdstempelkolom in de tabel, bijvoorbeeld wat een tijdstempel 2014-07-01 15:00:00 vertegenwoordigt in feite, is het een "wintertijd" of een "zomertijd"?

CURRENT_TIMESTAMP functie retourneert een waarde van het datatype TIMESTAMP WITH TIME ZONE
zie deze link:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm

Terwijl door tijdstempels en datums te vergelijken, converteert Oracle impliciet de gegevens naar het meer precieze gegevenstype met behulp van de sessietijdzone!
Zie deze link --> http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251

In ons specifieke geval werpt Oracle timestamp kolom naar de timestamp with time zone type.

Oracle bepaalt een sessietijdzone vanuit de clientomgeving.
U kunt de huidige sessietijdzone bepalen met deze query:

select sessiontimezone from dual;

Als op mijn pc (Win 7) bijvoorbeeld de optie ""Klok automatisch aanpassen voor zomertijd" is aangevinkt, geeft deze query als resultaat (onder SQLDeveloper):

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


Als ik deze optie in Windows uitschakel en vervolgens SQLDeveloper herstart, krijg ik:

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

De voormalige sessietijdzone is een tijdzone met een regionaam, waarvoor Oracle de zomertijdregels voor deze regio gebruikt in datumberekeningen:

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       


De laatste tijdzone gebruikt een vaste offset "+01:00" (altijd de "Wintertijd"), en Oracle past er geen DST-regels voor toe, het voegt gewoon de vaste offset toe.

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  

Houd er rekening mee dat, ter wille van de nieuwsgierigheid, Y resultaten in het bovenstaande vertegenwoordigen twee verschillende tijden !!!
014-05-29 01:30:00 EUROPE/BELGRADE is niet hetzelfde als:2014-05-29 01:30:00 +01:00

maar eigenlijk dit:
014-05-29 01:30:00 EUROPE/BELGRADE is gelijk aan:2014-05-29 01:30:00 +02:00

Het bovenstaande is alleen bedoeld om u ervan bewust te maken hoe eenvoudig het "uitvinken van vakjes" van invloed kan zijn op uw zoekopdrachten, en waar u moet zoeken naar een reden als gebruikers klagen dat "deze zoekopdracht in januari prima werkte, maar verkeerde resultaten in juli".

En nog steeds over het onderwerp ORA-01878 - laten we zeggen dat mijn sessie EUROPE/Warsaw is en mijn tabel bevat deze tijdstempel (zonder tijdzone)

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

Merk op dat in mijn regio de zomertijdverandering, in 2014, plaatsvindt op 30 maart om 2.00 uur.
Het betekent gewoon dat ik op 30 maart, om 2.00 uur 's nachts, wakker moet worden en mijn horloge moet verschuiven vooruit van 2:00 tot 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 weet dat dit tijdstempel niet geldig is in mijn regio volgens de zomertijdregels, omdat er geen tijd is 2:30 op 30 maart - om 2:00 wordt de klok verplaatst naar 3:00, en er is geen tijd 2:30. Daarom geeft Oracle de fout ORA-01878.

Deze query werkt echter prima:

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 

En dit is een reden voor deze fout - uw tabel bevat tijdstempels zoals 2014-03-09 2:30 of zo (voor New York, waar zomertijdverschuivingen plaatsvinden op 9 maart en 2 november), en Oracle weet niet hoe ze ze moeten converteren van tijdstempel (zonder TZ) naar tijdstempel met TZ.

De laatste vraag - waarom de vraag met >= werkt niet, maar de zoekopdracht met <= werkt prima ?

Ze werken wel/niet werken, omdat SQLDeveloper alleen de eerste 50 rijen retourneert (misschien 100? Het hangt af van de instellingen). De query leest niet de hele tabel, maar stopt wanneer de eerste 50(100) rijen worden opgehaald.
Verander de "werkende" query in bijvoorbeeld:

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

Dit dwingt de query om alle rijen in de tabel te lezen, en de fout zal verschijnen, ik ben er 100% zeker van.



  1. Zoek naar een bepaalde string in de Oracle clob-kolom

  2. Hoe pg_sleep_until() werkt in PostgreSQL

  3. php mysqli_connect:authenticatiemethode onbekend bij de client [caching_sha2_password]

  4. Hoe OCT() werkt in MariaDB