Oracle
 sql >> база данни >  >> RDS >> Oracle

Сравнението на датата на Oracle е нарушено поради DST

За да избегнете тази грешка, помислете за използването на изрично прехвърляне на израза в клаузата where към тип времеви клеймо (клеймо за време без часова зона) по този начин:

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

Като алтернатива можете изрично да зададете часовата зона на сесията например '-05:00' - за стандартно (зимно) време в Ню Йорк,
като използвате ALTER SESSION time_zone = '-05:00' , или като зададете променлива на средата ORA_SDTZ във всички среди на клиента,
вижте тази връзка за подробности:http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG263

Но зависи и от това каквонаистина се съхранява в колоната за времеви отпечатъци в таблицата, например какво клеймо за време 2014-07-01 15:00:00 всъщност представлява "зимно време" или "лятно време"?

CURRENT_TIMESTAMP функцията връща стойност от тип данни TIMESTAMP С ЧАСОВА ЗОНА
вижте тази връзка:http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions037.htm

Докато Сравнявайки времеви марки и дати, Oracle имплицитно преобразува данните в по-точен тип данни използвайки часовата зона на сесията!
Вижте тази връзка --> http://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG251

В нашия конкретен случай Oracle предава timestamp колона към timestamp with time zone тип.

Oracle определя часова зона на сесията от клиентската среда.
Можете да определите текущата часова зона на сесията, като използвате тази заявка:

select sessiontimezone from dual;

Например на моя компютър (Win 7), когато опцията ""Автоматично регулиране на часовника за лятно часово време" е отметнала, тази заявка връща (под SQLDeveloper):

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


Когато махна отметката от тази опция в Windows и след това рестартирам SQLDeveloper, тя дава:

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

Предишната часова зона на сесията е часова зона с име на регион, за която Oracle използва правилата за лятно часово време за този регион при изчисляване на датите:

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       


Последната часова зона използва фиксирано отместване „+01:00“ (винаги „зимно време“) и Oracle не прилага никакви DST правила за нея, а просто добавя фиксираното отместване.

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  

Моля, имайте предвид, от любопитство, че Y резултатите от горното представляват две различни времена !!!
014-05-29 01:30:00 EUROPE/BELGRADE не е същото като:2014-05-29 01:30:00 +01:00

но всъщност това:
014-05-29 01:30:00 EUROPE/BELGRADE е равно на:2014-05-29 01:30:00 +02:00

Посоченото по-горе е само за да ви накара да разберете колко простото „премахване на отметката“ може да повлияе на вашите заявки и къде да търсите причина, когато потребителите се оплакват „тази заявка работи добре през януари, но даде грешни резултати през юли".

И все пак по темата ORA-01878 - да кажем, че моята сесия е EUROPE/Warsaw и моята таблица съдържа това клеймо за време (без часова зона)

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

Обърнете внимание, че в моя регион промяната на DST през 2014 г. се случва на 30 март в 2:00 часа сутринта
Това просто означава, че на 30 март, в 2:00 през нощта, трябва да се събудя и да сменя часовника си напред от 2:00 до 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 знае, че това клеймо за време не е валидно в моя регион според DST правилата, защото на 30 март няма час 2:30 - в 2:00 часовникът се премества на 3:00, а няма час 2:30. Следователно Oracle извежда грешката ORA-01878.

Тази заявка обаче работи перфектно:

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 

И това е причината за тази грешка - вашата таблица съдържа времеви печати като 2014-03-09 2:30 или така (за Ню Йорк, където смените на DST се случват на 9 март и 2 ноември), а Oracle не знае как да ги преобразува от времеви печат (без TZ) в времеви печат с TZ.

Последният въпрос - защо заявката с >= не работи, но заявката с <= работи добре ?

Те работят/не работят, защото SQLDeveloper връща само първите 50 реда (може би 100? Зависи от настройките). Заявката не чете цялата таблица, спира при извличане на първите 50(100) реда.
Променете „работещата“ заявка на, например:

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

Това принуждава заявката да прочете всички редове в таблицата и грешката ще се появи, 100% съм сигурен.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 4 начина за намиране на редове, които съдържат главни букви в Oracle

  2. Заявка за намиране на пълни сканирания на таблицата в oracle

  3. Представяне на IPv4/IPv6 адреси в Oracle

  4. _gc_fusion_compression

  5. Преброяване на броя на съединените редове в лявото присъединяване