sql >> Databáze >  >> RDS >> Oracle

Porovnání dat Oracle bylo přerušeno kvůli letnímu času

Chcete-li se této chybě vyhnout, zvažte použití explicitního přetypování výrazu v klauzuli where na typ časového razítka (časové razítko bez časového pásma), a to tímto způsobem:

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

Případně můžete explicitně nastavit časové pásmo relace na, například '-05:00' - pro newyorský standardní (zimní) čas,
pomocí ALTER SESSION time_zone = '-05:00' , nebo nastavením proměnné prostředí ORA_SDTZ ve všech klientských prostředích,
podrobnosti naleznete na tomto odkazu:http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

Ale záleží také na tom, co skutečně je uloženo ve sloupci časové razítko v tabulce, například jaké časové razítko 2014-07-01 15:00:00 ve skutečnosti představuje, je to „zimní čas“ nebo „letní čas“?

CURRENT_TIMESTAMP funkce vrací hodnotu datového typu TIMESTAMP WITH TIME ZONE
viz tento odkaz:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm

While při porovnání časových razítek a dat Oracle implicitně převádí data na přesnější datový typ pomocí časového pásma relace !
Viz tento odkaz --> http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251

V našem konkrétním případě Oracle vrhá timestamp do timestamp with time zone type.

Oracle určuje časové pásmo relace z prostředí klienta.
Aktuální časové pásmo relace můžete určit pomocí tohoto dotazu:

select sessiontimezone from dual;

Například na mém PC (Win 7), když je zaškrtnuta možnost ""Automaticky upravit hodiny pro letní čas", vrátí tento dotaz (pod SQLDeveloper):

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


Když zruším zaškrtnutí této možnosti ve Windows a poté restartuji SQLDeveloper, zobrazí se:

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

Dřívější časové pásmo relace je časové pásmo s názvem regionu, pro které Oracle používá pravidla letního času pro tuto oblast při výpočtech 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       


Poslední časové pásmo používá pevný posun "+01:00" (vždy "zimní čas") a Oracle pro něj nepoužívá žádná pravidla DST, pouze přidává pevný posun.

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  

Všimněte si prosím pro zajímavost, že Y výsledky výše představují dva různé časy !!!
014-05-29 01:30:00 EUROPE/BELGRADE není totéž jako:2014-05-29 01:30:00 +01:00

ale ve skutečnosti toto:
014-05-29 01:30:00 EUROPE/BELGRADE se rovná:2014-05-29 01:30:00 +02:00

Výše uvedené slouží pouze k tomu, abyste si uvědomili, jak jednoduché „zrušení zaškrtnutí políčka“ může ovlivnit vaše dotazy, a kde hledat důvod, když si uživatelé stěžují „tento dotaz fungoval v lednu dobře, ale špatné výsledky v červenci“.

A ještě k tématu ORA-01878 - řekněme, že moje relace je EUROPE/Warsaw a moje tabulka obsahuje toto časové razítko (bez časového pásma)

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

Všimněte si, že v mém regionu ke změně letního času v roce 2014 dochází 30. března ve 2:00 ráno
Jednoduše to znamená, že 30. března ve 2:00 v noci se musím probudit a posunout si hodinky vpřed od 2:00 do 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 ví, že toto časové razítko není v mém regionu platné podle pravidel DST, protože 30. března není čas 2:30 - ve 2:00 se hodiny posunou na 3:00 a není čas 2:30. Proto Oracle vyvolá chybu ORA-01878.

Tento dotaz však funguje naprosto v pořádku:

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 

A to je důvod této chyby – vaše tabulka obsahuje časová razítka jako 2014-03-09 2:30 nebo tak (pro New York, kde k posunům letního času dochází 9. března a 2. listopadu) a Oracle neví, jak je převést z časového razítka (bez TZ) na časové razítko s TZ.

Poslední otázka - proč dotaz s >= nefunguje, ale dotaz s <= funguje dobře ?

Fungují/nefungují, protože SQLDeveloper vrací pouze prvních 50 řádků (možná 100? Záleží na nastavení). Dotaz nečte celou tabulku, zastaví se při načtení prvních 50 (100) řádků.
Změňte "pracovní" dotaz například na:

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

To přinutí dotaz číst všechny řádky v tabulce a chyba se objeví, jsem si 100% jistý.



  1. Jaký je význam otazníku v MySQL ve sloupci WHERE =??

  2. Vraťte seznam vypočítaných sloupců na serveru SQL Server

  3. Jak COALESCE() funguje v MariaDB

  4. Jak vložit C# List do databáze pomocí Dapper.NET