【问题标题】:How to grab the value for the previous hour when the data type is TIMESTAMP with TIMEZONE当数据类型为 TIMESTAMP 和 TIMEZONE 时如何获取前一小时的值
【发布时间】:2018-08-06 17:49:35
【问题描述】:

因此,如果满足条件,我有一些逻辑将尝试获取与前一小时相关的值 (VALUE)。 HOUR 列是带有 TIME ZONE 列的 TIMESTAMP。我想我可以运行以下查询,但得到 ORA-00932 不一致的数据类型:预期 TIMESTAMP WITH TIME ZONE got NUMBER 错误。我必须将某种转换功能添加到我的“带时区的时间戳”值中吗?

下面是我的查询代码:

SELECT MAX(VALUE)
  FROM VALUE V
 WHERE CODE = 'HI'
   AND HR = '15-JAN-17 05.00.00.000000000 AM' - (1/24);

提前致谢。

【问题讨论】:

    标签: sql oracle


    【解决方案1】:

    '15-JAN-17 05.00.00.000000000 AM' 是一个字符串,而不是时间戳。您可以按照@D-Shih 的建议将其转换为时间戳(没有时区),但您应该指定格式掩码和日期语言,而不是依赖 NLS 设置:

    AND HR = to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
     'NLS_DATE_LANGUAGE=ENGLISH') - (1/24);
    

    或者如果它是一个固定值(可能不是,或者你可以改变那个文字):

    AND HR = timestamp '2017-01-15 05:00:00' - (1/24);
    

    从时间戳中减去天数会给你一个日期结果,所以你可能真的想这样做:

    AND HR = timestamp '2017-01-15 05:00:00' - interval '1' hour;
    

    这现在保留为时间戳,但您没有时区信息。如果您知道时区,则可以将其包含在字符串文字和格式掩码中,或包含在时间戳文字中,例如:

    AND HR = timestamp '2017-01-15 05:00:00 America/Los_Angeles' - (1/24);
    

    或者来自您的原始字符串,如果您只需要使用这些,您可以使用from_tz():

    AND HR = from_tz(to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
      'NLS_DATE_LANGUAGE=ENGLISH'), 'America/Los_Angeles') - interval '1' hour;
    

    最后做间隔减法应该意味着它正确处理了夏令时。

    各种转换的演示,从您的字符串值开始:

    alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
    alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF1';
    alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF1 TZR TZD';
    
    select
      to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
        'NLS_DATE_LANGUAGE=ENGLISH') as a_timestamp,
      to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
        'NLS_DATE_LANGUAGE=ENGLISH') - (1/24) as b_date,
      to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
        'NLS_DATE_LANGUAGE=ENGLISH') - interval '1' hour as c_timestamp,
      from_tz(to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
      'NLS_DATE_LANGUAGE=ENGLISH'), 'America/Los_Angeles') - interval '1' hour as d_timestamp_tz
    from dual;
    
    A_TIMESTAMP           B_DATE              C_TIMESTAMP           D_TIMESTAMP_TZ                               
    --------------------- ------------------- --------------------- ---------------------------------------------
    2017-01-15 05:00:00.0 2017-01-15 04:00:00 2017-01-15 04:00:00.0 2017-01-15 04:00:00.0 AMERICA/LOS_ANGELES PST
    

    【讨论】:

    • 谢谢!所以问题:如果我要通过复制和粘贴来引用特定的时间戳,如果我要将它添加到我的 WHERE 子句中,我还需要转换它吗?
    • 如果您将其复制并粘贴为字符串,则可以。您可能会摆脱隐式转换,但最好不要依赖它。 (如果该值来自另一个表,那么您应该加入,并将其保留为时间戳而不是字符串。但显然不知道它实际来自哪里。)
    • 感谢@AlexPoole。我需要什么来定义时间戳格式,就好像日期/时间戳可以是 AM 和 FM? 'DD-MON-RR HH.MI.SS.FF AM/PM'?我试过了,它似乎没有用。
    • 就用我展示的。在格式掩码中包含 AM 或 PM(不是两者)将识别字符串 vakue 中的 AM 或 PM。
    • 查看之前的评论 - HR 已经是带有时区的时间戳,因此您不需要或不需要to_timestamp()from_tz()。我不明白你想做什么。问题是关于您想将其视为时间戳的字符串值,而不是关于已经时间戳的内容。
    【解决方案2】:

    使用TO_TIMESTAMP'15-JAN-17 05.00.00.000000000 AM' 到日期时间,然后减去一小时。

    SELECT MAX(VALUE)
      FROM VALUE V
     WHERE CODE = 'HI'
       AND HR =  TO_TIMESTAMP('15-JAN-17 05.00.00.000000000 AM','DD-MON-RR HH.MI.SS.FF AM') - (1/24);
    

    【讨论】:

    • 如果我想做 HR = from_TZ(TO_TIMESTAMP((HR) - (1/24), 'UTC') 怎么办?我尝试这个时出错。
    • Sind HR 是一个 TIMESTAMP WITH TIME ZONE 数据类型,查询实际上是 SYS_EXTRACT_UTC(HR) = SYS_EXTRACT_UTC(FROM_TZ(TO_TIMESTAMP('15-JAN-17 05.00.00.000000000 AM'), SESSIONTIMEZONE) - (1/24)) - 这就是您需要的吗?
    • 在没有明确格式定义的情况下使用TO_TIMESTAMP() 总是一个坏主意。
    • @WernfriedDomscheit 是的,你的建议是对的,谢谢你的提醒
    • 所以我要做的是获取一个转换后的值(HR 中出现值的前一小时)。我试过 /*select hr, hr - (1/24) as CONVERTED from value */
    猜你喜欢
    • 2018-04-09
    • 2019-07-21
    • 2017-03-19
    • 1970-01-01
    • 1970-01-01
    • 2014-09-29
    • 2019-12-19
    • 1970-01-01
    • 2013-12-20
    相关资源
    最近更新 更多