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