Oracle странное поведение при группировке по полю TIMESTAMP, когда значения находятся на границе летнего времени - PullRequest
0 голосов
/ 21 ноября 2018

У меня странное поведение БД Oracle с TIMESTAMP и переходом на летнее время.

Следующий запрос дает разные результаты при группировании по разным столбцам, и не понятно, почему.

При группировании по my_date_ny_ts возвращает две разные строки, а когда по my_date_ny_ts_tz - результат - только одна строка (для меня правильная).

Обратите внимание, что значения предназначены для11/04/2018 00:00:00 -05: 00 и 11/04/2018 00:00:00 -06: 00, которые преобразованы в часовой пояс New_York в 11-04-2018 01:00 и 02:00 ПО ВОСТОЧНОМУ ВРЕМЕНИкоторые на самом деле 01:00 EDT и 01:00 EST.

Я понимаю, почему эти значения разные, но они должны быть равны после преобразования их в TIMESTAMP без типа данных часового пояса ( my_date_ny_ts столбец), поскольку этот тип не содержит никакой информации о часовом поясе и состоянии летнего времени (см. Значения tdz_ny_ts ).Только после того, как я преобразовал значения обратно в тип TIMESTAMP WITH TIMEZONE ( my_date_ny_ts_tz ), они становятся равными.Мне не нужен обходной путь (уже есть), просто интересно, является ли это поведение ошибкой Oracle или по недоразумению:

select count(*), my_date_ny_ts_tz from (

SELECT
 mydate, -- timestamp with timezone
 to_char( mydate, 'TZD') as tdz, -- daylight savings flag - VALUES are NULL because timezone is an offset
 mydate AT TIME ZONE 'America/New_York' AS mydate_ny, -- timestamp with timezone in EST timezone
 to_char( mydate AT TIME ZONE 'America/New_York', 'TZD') as tdz_ny, --timestamp with timezone in EST timezone -  daylight savings flag - RETURNS EDT FOR ONE EST FOR SECOND - RIGHT
cast(mydate AT TIME ZONE 'America/New_York' as timestamp)  as my_date_ny_ts,--cast to timestamp without timezone - GROUP BY RETURNS TWO ROWS - BUG?
to_char( cast(mydate AT TIME ZONE 'America/New_York' as timestamp) , 'TZD') as tdz_ny_ts,--cast to timestamp without timezone -  daylight savings flag - both values are null so why the group by on the prev field doesn't work?
 cast(mydate AT TIME ZONE 'America/New_York' as timestamp) AT TIME ZONE 'America/New_York' as my_date_ny_ts_tz,--cast back to timestamp with timezone in EST timezone - NOW GROUP BY RETURNS ONE ROW
 to_char( cast(mydate AT TIME ZONE 'America/New_York' as timestamp) AT TIME ZONE 'America/New_York', 'TZD') as tdz_ny_ts_tz--daylight savings flag of the prev field - both are EST - RIGHT
 FROM
     (
         SELECT
         to_timestamp_tz('11/04/2018 00:00:00 -05:00','mm/dd/yyyy hh24:mi:ss TZH:TZM') AS mydate
     FROM
         dual
     UNION
     SELECT
         to_timestamp_tz('11/04/2018 00:00:00 -06:00','mm/dd/yyyy hh24:mi:ss TZH:TZM') AS mydate
     FROM
         dual
 )
 ) group by my_date_ny_ts_tz

Моя версия следующая, но это также происходит в 12c:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
"CORE   11.2.0.4.0  Production"
TNS for 64-bit Windows: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

Спасибо

1 Ответ

0 голосов
/ 21 ноября 2018

Может быть ошибка в Oracle.Вы можете сократить запрос до этого:

SELECT 
    TO_CHAR(CAST(TIMESTAMP '2018-11-04 00:00:00 -05:00' AT TIME ZONE 'America/New_York' AS TIMESTAMP), 'yyyy-mm-dd hh24:mi:ss') AS my_date_ny_ts,
    DUMP(CAST(TIMESTAMP '2018-11-04 00:00:00 -05:00' AT TIME ZONE 'America/New_York' AS TIMESTAMP)) AS my_date_ny_ts_dump
FROM DUAL 
UNION ALL
SELECT
    TO_CHAR(CAST(TIMESTAMP '2018-11-04 00:00:00 -06:00' AT TIME ZONE 'America/New_York' AS TIMESTAMP), 'yyyy-mm-dd hh24:mi:ss'),
    DUMP(CAST(TIMESTAMP '2018-11-04 00:00:00 -06:00' AT TIME ZONE 'America/New_York' AS TIMESTAMP))
FROM DUAL;


+--------------------------------------------------------------------------------------+
|MY_DATE_NY_TS      |MY_DATE_NY_TS_DUMP                                                |
+--------------------------------------------------------------------------------------+
|2018-11-04 01:00:00|Typ=187 Len=20: 226,7,11,4,1,0,0,0,0,0,0,0,252,0,3,0,100,0,11,3   |
|2018-11-04 01:00:00|Typ=187 Len=20: 226,7,11,4,1,0,0,48,0,0,0,0,251,0,3,44,100,0,48,44|
+--------------------------------------------------------------------------------------+

Как видите, значения меток времени одинаковы, однако значения DUMP() различны, т.е. вы получите две строки, если наберете GROUP BY.

Вы можете запустить его немного по-другому.На самом деле я ожидал бы того же результата, что и выше (независимо от того, считаете ли вы это правильным или нет), но он отличается:

WITH t AS
    (SELECT 
        CAST(TIMESTAMP '2018-11-04 00:00:00 -05:00' AT TIME ZONE 'America/New_York' AS TIMESTAMP) AS my_date_ny_ts
    FROM DUAL 
    UNION ALL
    SELECT
        CAST(TIMESTAMP '2018-11-04 00:00:00 -06:00' AT TIME ZONE 'America/New_York' AS TIMESTAMP)
    FROM DUAL)
SELECT TO_CHAR(my_date_ny_ts, 'yyyy-mm-dd hh24:mi:ss') AS my_date_ny_ts,
    DUMP(my_date_ny_ts) AS my_date_ny_ts_dump
FROM t;

    +--------------------------------------------------------------------------------------+
|MY_DATE_NY_TS      |MY_DATE_NY_TS_DUMP                                                |
+--------------------------------------------------------------------------------------+
|2018-11-04 01:00:00|Typ=180 Len=7: 120,118,11,4,2,1,1                                 |
|2018-11-04 01:00:00|Typ=180 Len=7: 120,118,11,4,2,1,1                                 |
+--------------------------------------------------------------------------------------+

Это выглядит странно для меня.Хотя я сделал CAST(... AS TIMESTAMP) для обоих, как только я получаю Typ=187, и как только я получаю Typ=180 в DUMP.

Похоже, что тип SQL TIMESTAMP 180 (см. Встроенные типы данных Oracle ) ведет себя иначе, чем тип PL / SQL TIMESTAMP 187 (см. ПАКЕТ SYS.dbms_types) -но я не знаю почему.

В качестве обходного пути я бы предложил использовать функцию SYS_EXTRACT_UTC(...) вместо CAST(... AS TIMESTAMP).

...