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

Oracle datosammenligning brudt på grund af sommertid

For at undgå denne fejl skal du overveje at bruge en eksplicit cast af udtrykket i where-sætningen til en tidsstempeltype (tidsstempel uden tidszone), på denne måde:

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

Alternativt kan du udtrykkeligt indstille sessionens tidszone til for eksempel '-05:00' - for New York standard (vinter) tid,
ved at bruge ALTER SESSION time_zone = '-05:00' , eller ved at indstille ORA_SDTZ miljøvariabel i alle klientens miljøer,
se dette link for detaljer:http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

Men det afhænger også af hvad egentlig er gemt i tidsstempelkolonnen i tabellen, for eksempel hvad et tidsstempel 2014-07-01 15:00:00 repræsenterer faktisk, er det en "vintertid" eller en "sommertid"?

CURRENT_TIMESTAMP funktion returnerer en værdi af datatypen TIMESTAMP WITH TIME ZONE
se dette link:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm

Mens ved at sammenligne tidsstempler og datoer konverterer Oracle implicit dataene til den mere præcise datatype ved hjælp af sessionens tidszone !
Se dette link --> http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251

I vores særlige tilfælde kaster Oracle timestamp kolonne til timestamp with time zone type.

Oracle bestemmer en sessionstidszone fra klientmiljøet.
Du kan bestemme den aktuelle sessionstidszone ved hjælp af denne forespørgsel:

select sessiontimezone from dual;

For eksempel på min pc (Win 7), når indstillingen ""Juster automatisk ur til sommertid" er markeret, returnerer denne forespørgsel (under SQLDeveloper):

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


Når jeg fjerner markeringen af ​​denne mulighed i Windows og derefter genstarter SQLDeveloper, giver det:

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

Den tidligere sessionstidszone er en tidszone med et områdenavn, som Oracle bruger sommertidsreglerne for denne region til i datoberegninger:

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       


Sidstnævnte tidszone bruger en fast offset "+01:00" (altid "vintertid"), og Oracle anvender ingen sommertidsregler for det, det tilføjer blot den faste offset.

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  

Bemærk venligst, for nysgerrighedens skyld, at Y resultater i ovenstående repræsenterer to forskellige tidspunkter !!!
014-05-29 01:30:00 EUROPE/BELGRADE er ikke det samme som:2014-05-29 01:30:00 +01:00

men faktisk dette:
014-05-29 01:30:00 EUROPE/BELGRADE er lig med:2014-05-29 01:30:00 +02:00

Ovenstående er kun for at gøre dig opmærksom på, hvor simpelt "fjernelse af afkrydsning" kunne påvirke dine forespørgsler, og hvor man kan grave af en grund, når brugere klager "denne forespørgsel fungerede fint i januar, men gav forkerte resultater i juli".

Og stadig om emnet ORA-01878 - lad os sige, at min session er EUROPE/Warsaw og min tabel indeholder dette tidsstempel (uden tidszone)

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

Bemærk, at sommertidsændringen i min region i 2014 sker den 30. marts kl. 02.00.
Det betyder simpelthen, at den 30. marts kl. 02.00 om natten skal jeg vågne op og skifte vagt frem fra 2:00 til 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 ved, at dette tidsstempel ikke er gyldigt i min region efter sommertidsreglerne, for der er ingen tid 2:30 den 30. marts - klokken 2:00 flyttes klokken til 3:00, og der er ingen tid 2:30. Derfor kaster Oracle fejlen ORA-01878.

Men denne forespørgsel fungerer perfekt:

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 

Og dette er årsagen til denne fejl - din tabel indeholder tidsstempler som 2014-03-09 2:30 eller deromkring (for New York, hvor sommertid-skift finder sted den 9. marts og den 2. november), og Oracle ved ikke, hvordan man konverterer dem fra tidsstempel (uden TZ) til tidsstempel med TZ.

Det sidste spørgsmål - hvorfor forespørgslen med >= virker ikke, men forespørgslen med <= fungerer fint?

De virker/virker ikke, fordi SQLDeveloper kun returnerer de første 50 rækker (måske 100? Det afhænger af indstillinger). Forespørgslen læser ikke hele tabellen, den stopper, når de første 50(100) rækker er hentet.
Ændr den "fungerende" forespørgsel til f.eks.:

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

Dette tvinger forespørgslen til at læse alle rækker i tabellen, og fejlen vises, jeg er 100 % sikker.



  1. Hvordan skriver man en REST API?

  2. Hvordan kan jeg dekryptere en adgangskodehash i PHP?

  3. JDBC returnerer tomt resultatsæt

  4. Sådan redigeres MySQL my.cnf-filen