programing

DST로 인해 Oracle 날짜 비교가 중단되었습니다.

easyjava 2023. 6. 13. 22:54
반응형

DST로 인해 Oracle 날짜 비교가 중단되었습니다.

최대 절전 모드를 통해 Java를 실행하는 앱 서버에서 실행되는 SQL 쿼리 문제를 디버깅했습니다.오류:

[3/10/14 10:52:07:143 EDT] 0000a984 JDBCException W org.hibernate.util.JDBCExceptionReporter logExceptions SQL Error: 1878, SQLState: 22008
[3/10/14 10:52:07:144 EDT] 0000a984 JDBCException E org.hibernate.util.JDBCExceptionReporter logExceptions ORA-01878: specified field not found in datetime or interval

아래의 간단한 SQL로 범위를 좁힐 수 있었습니다.

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

동일한 데이터베이스에서 이를 실행하면 다음 오류가 발생합니다.

ORA-01878: specified field not found in datetime or interval
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.

MY_TIMESTAMP열은 다음과 같이 정의됩니다.TIMESTAMP(6).

FWIW, 위의 SQL에서 비교를 변경하면>=<=쿼리가 작동합니다.

시간 변경(미국/뉴욕에 있음)과 관련이 있다고 가정하지만 디버깅을 사용하여 여기서 어디로 이동해야 하는지 파악하는 데 문제가 있습니다.

또한 MyBatis를 통해 실행 중인 유사한 쿼리에서 이 문제가 발견되었으며 다음과 같은 오류가 발생했습니다.

### Error querying database.  Cause: java.sql.SQLException: ORA-01878: specified field not found in datetime or interval

### The error may involve defaultParameterMap
### The error occurred while setting parameters
### Cause: java.sql.SQLException: ORA-01878: specified field not found in datetime or interval

업데이트: Windows의 동료가 "여름 시간에 맞춰 시계를 자동으로 조정"의 선택을 취소하여 Windows 날짜 및 시간 설정을 변경한 다음 새 SQL Developer 인스턴스를 열었습니다.두 번째 인스턴스는 문제 없이 쿼리를 실행할 수 있지만 첫 번째 인스턴스(이전 DST 설정 포함)는 여전히 실패합니다.

이 오류를 방지하려면 다음과 같은 방법으로 where 절의 표현식을 타임스탬프 유형(시간대가 없는 타임스탬프)에 명시적으로 캐스팅하는 것을 고려하십시오.

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

뉴욕 05으로할 수 . 예를 들어 05:00'은 '-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 TIME ZONE function합니다.
다음 링크를 참조하십시오. 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

경우 은 우리특경우, 은클오라를 합니다.timestamp의럼의 합니다.timestamp with time zone활자를

Oracle은 클라이언트 환경에서 세션 시간대를 결정합니다.
다음 쿼리를 사용하여 현재 세션 시간대를 확인할 수 있습니다.

select sessiontimezone from dual;

예를 들어 내 PC(Win 7)에서 "여름 시간에 맞춰 시계를 자동으로 조정" 옵션을 선택하면 이 쿼리는 (SQL Developer에서) 다음을 반환합니다.

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


Windows에서 이 옵션을 선택 취소한 다음 SQL Developer를 다시 시작하면 다음과 같은 메시지가 나타납니다.

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

위의 내용은 간단한 "상자 확인 해제"가 어떻게 쿼리에 영향을 미칠 수 있는지, 사용자가 "이 쿼리가 1월에는 잘 작동했지만 7월에는 잘못된 결과를 제공했다"고 불평할 때 이유를 어디서 찾아야 하는지 알려주기 위한 것입니다.


- 내 이 ORA-01878이라고 .EUROPE/Warsaw 내 이 타임스탬프 없음가 .

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

제 지역에서는 2014년 DST 변경이 3월 30일 새벽 2시에 발생합니다.
30일 월것단일 3시 30시시,에 2에서 2로 3다겨 3야니 3는합;)

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 규칙에 따라 내 지역에서 유효하지 않다는 것을 알고 있습니다. 왜냐하면 3월 30일 2시 30분 - 2시에는 시계가 3시로 이동되고 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 ) (3/9/2/DST/Oracle/TZ의 경우)


마지막 질문 - 왜 쿼리를 사용하는지에 대한 이유>= 작하지않다니가 쿼리입니다. 하지만 쿼리는 사용할 수 없습니다.<=합니까? 잘 작합니까동?

는 처음 100SQL Developer 처 50 개행아반환개기때하문작작동동않하습하지나거니다에마도음만는개아개▁100▁theymaybe않 100▁100▁().설정에 따라 다릅니다.쿼리는 전체 테이블을 읽지 않고 처음 50개(100) 행을 가져올 때 중지됩니다.
과 같이 합니다. 를 들어 " " " " " " " " " " 입니다.

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

이렇게 하면 쿼리가 테이블의 모든 행을 읽게 되고 오류가 나타납니다. 100% 확실합니다.

Kordirko에게 매우 상세하게 작성해 주셔서 감사합니다.제 생각에 앞으로 우리는 실수하기 쉬운 날짜를 비교하는 다른 방법을 찾을 것입니다.그동안 우리는 문제를 파악할 수 있었고 일시적이고 장기적인 해결책도 찾을 수 있었습니다.

먼저, 이슈에 대한 자세한 내용입니다.데이터베이스의 TIMESTAMP 필드에 저장 중인 값이 올바르지 않은 것으로 나타났습니다.우리는 덤프 기능을 사용하고 바이트를 조사함으로써 이것을 보았습니다.아래 출력에서 5번째 바이트를 보면 시간이 표시됩니다(실제 시간 + 1이므로 5는 실제로 오전 4시).오전 3시에서 4시 사이의 값의 경우, 5번째 바이트는 3이며 이는 오전 2시를 나타냅니다. EST에서 2014년 3월 9일 오전 2시는 존재하지 않습니다. DST 규칙 및 Oracle 규칙에 따르면 잘못된 시간입니다.

09-MAR-14 03.06.21.522000000 AM         Typ=180 Len=11: 120,114,3,9,3,7,22,31,29,22,128
09-MAR-14 03.32.37.869000000 AM         Typ=180 Len=11: 120,114,3,9,3,33,38,51,203,227,64
09-MAR-14 03.36.49.804000000 AM         Typ=180 Len=11: 120,114,3,9,3,37,50,47,236,17,0
09-MAR-14 03.43.47.328000000 AM         Typ=180 Len=11: 120,114,3,9,3,44,48,19,140,226,0
09-MAR-14 03.47.55.255000000 AM         Typ=180 Len=11: 120,114,3,9,3,48,56,15,50,253,192
09-MAR-14 03.55.45.129000000 AM         Typ=180 Len=11: 120,114,3,9,3,56,46,7,176,98,64
09-MAR-14 04.05.03.325000000 AM         Typ=180 Len=11: 120,114,3,9,5,6,4,19,95,27,64
09-MAR-14 04.28.41.267000000 AM         Typ=180 Len=11: 120,114,3,9,5,29,42,15,234,24,192
09-MAR-14 04.35.16.072000000 AM         Typ=180 Len=11: 120,114,3,9,5,36,17,4,74,162,0
09-MAR-14 04.41.07.260000000 AM         Typ=180 Len=11: 120,114,3,9,5,42,8,15,127,73,0
09-MAR-14 04.46.31.047000000 AM         Typ=180 Len=11: 120,114,3,9,5,47,32,2,205,41,192
09-MAR-14 04.53.33.471000000 AM         Typ=180 Len=11: 120,114,3,9,5,54,34,28,18,227,192

많은 연구와 논의 끝에 Oracle JDBC 드라이버(11.2.0.2) 버전이 잘못된 값을 삽입했을 수 있다는 사실에 주목했습니다.Oracle의 11.2.0.3 정보 페이지에서 "9785135 DST 변환이 jdbc 11g timestamz를 사용하여 정확하지 않음"과 같은 버그를 참조합니다.11.2.0.2와 11.2.0.3 드라이버를 모두 사용하여 2014년 3월 9일 오전 1:50부터 4:00까지 값을 삽입하는 빠른 테스트 클래스를 작성했습니다.다음은 DB에 삽입된 내용의 일부입니다.

DRIVER_V         JAVA_DATE_AS_STRING              ORACLE_TIMESTAMP                        DUMP(ORACLE_TIMESTAMP)
11.2.0.2.0/11/2  Sun Mar 09 01:50:00 EST 2014     09-MAR-14 01.50.00.000000000 AM         Typ=180 Len=7: 120,114,3,9,2,51,1
11.2.0.2.0/11/2  Sun Mar 09 03:00:00 EDT 2014     09-MAR-14 03.00.00.000000000 AM         Typ=180 Len=7: 120,114,3,9,3,1,1 --Invalid timestamp
11.2.0.3.0/11/2  Sun Mar 09 01:50:00 EST 2014     09-MAR-14 01.50.00.000000000 AM         Typ=180 Len=7: 120,114,3,9,2,51,1
11.2.0.3.0/11/2  Sun Mar 09 03:00:00 EDT 2014     09-MAR-14 03.00.00.000000000 AM         Typ=180 Len=7: 120,114,3,9,4,1,1 --Correct timestamp

오전 3시에 대한 두 번째 행의 타임스탬프 5번째 바이트가 올바르지 않습니다(3).이것은 11.2.0.2 버전을 사용하여 삽입되었습니다.11.2.0.3 버전과 함께 삽입된 동일한 값은 올바른 5번째 바이트(4)가 있는 4번째 줄에서 찾을 수 있습니다.

여기서 장기적인 해결책은 JDBC 드라이버를 업데이트하는 것입니다.여기서 단기적으로 수정한 것은 잘못된 값을 가진 행을 찾아 SQL Plus에서 해당 행에 대한 업데이트 문을 실행하여 시간을 다시 설정하는 것이었습니다(같은 값을 사용하지만 SQL Plus는 이 값을 올바르게 변환합니다).

언급URL : https://stackoverflow.com/questions/22305466/oracle-date-compare-broken-because-of-dst

반응형