【问题标题】:Oracle strange behavior when grouping by a TIMESTAMP field when the values are on the daylight saving border当值位于夏令时边界时,按 TIMESTAMP 字段分组时的 Oracle 奇怪行为
【发布时间】:2019-04-23 23:41:45
【问题描述】:

我对带有 TIMESTAMP 和夏令时的 Oracle DB 有一个奇怪的行为。

以下查询在按不同列分组时会产生不同的结果,原因尚不清楚。

当按 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 EDT,实际上是 01:00 EDT 和 01:00 EST。

我理解为什么这些值不同,但在将它们转换为 TIMESTAMP without time zone 数据类型(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

谢谢

【问题讨论】:

    标签: oracle timezone timestamp dst timestamp-with-timezone


    【解决方案1】:

    可能是 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),但一旦我在DUMP 中获得Typ=187Typ=180

    看起来 SQL 类型 TIMESTAMP180(请参阅 Oracle Built-In Data Types)与 PL/SQL 类型 TIMESTAMP187(请参阅 PACKAGE SYS.dbms_types)的行为不同 - 但我不知道为什么。

    对于解决方法,我建议使用函数SYS_EXTRACT_UTC(...) 而不是CAST(... AS TIMESTAMP)

    【讨论】:

      猜你喜欢
      • 2012-03-20
      • 1970-01-01
      • 2014-04-18
      • 1970-01-01
      • 2013-03-29
      • 2019-05-26
      • 1970-01-01
      • 2013-10-24
      • 2015-10-20
      相关资源
      最近更新 更多