当数据类型为 TIMESTAMP 和 TIMEZONE 时如何获取前一小时的值

Posted

技术标签:

【中文标题】当数据类型为 TIMESTAMP 和 TIMEZONE 时如何获取前一小时的值【英文标题】:How to grab the value for the previous hour when the data type is TIMESTAMP with 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);

提前致谢。

【问题讨论】:

【参考方案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 */

以上是关于当数据类型为 TIMESTAMP 和 TIMEZONE 时如何获取前一小时的值的主要内容,如果未能解决你的问题,请参考以下文章

数据库字段类型timeStamp应对应.net实体类的啥类型???

MySQL数据类型 - 日期和时间类型

java中如何将“2014-07-07”保存到数据类型为Timestamp类型的数据库中

mysql里时间类型为timestamp 怎么将它截取只显示年月日

datetime和timestamp的区别

mysql中date,datetime,timestamp数据类型区别