Oracle - 将日期列与 sysdate 进行比较

Posted

技术标签:

【中文标题】Oracle - 将日期列与 sysdate 进行比较【英文标题】:Oracle - compare date column to sysdate 【发布时间】:2020-02-04 09:03:30 【问题描述】:

我有一个包含日期的 nvarchar2 列的 talbe。 (我们称之为 my_date - 模式:DD-MM-YYYY HH24:MI:SS) 列类型是数字(19),里面的值是纪元时间,我这样转换:

select TO_CHAR(FROM_TZ(CAST(DATE '1970-01-01' + (1/24/60/60/1000) * my_date AS TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York', 'DD-MM-YYYY HH24:MI:SS') as my_date
from my_table

我想将它与 sysdate 进行比较,但由于未知原因,它似乎不起作用。

select my_date
from my_table
where mydate >= TO_CHAR(sysdate-(1/24),'DD-MM-YYYY HH24:MI:SS')

我也做过:

select dump (TO_CHAR(sysdate-(1/24),'DD-MM-YYYY HH24:MI:SS')) from my_table
select dump (my_date) from my_table

在这两种情况下我都得到“typ=1”

有什么想法吗?

提前致谢。

为了更清楚: my_date 中的起始值为 = 1580801246921 使用这个网站来了解它的价值,以确保我正确转换它:https://www.epochconverter.com/

【问题讨论】:

您不应将 DATE 值存储在 varchar 列中。你有可能解决这个问题吗? 遗憾的是,我没有,它是从其他产品存储在数据库中的数据...该列中的实际数据是我转换的纪元时间,现在尝试比较其中的日期到系统日期 纪元时间如何存储 - 实际上是作为字符串或数字;以及您如何从您拥有的任何实际值获得您所指的“格式化”值? (关于如何从 epoch/'Unix' 时间转换为 Oracle 日期已经有很多问题了。)请在您的问题中包括一些示例数据和相关的表结构)实际数据类型等。 谢谢,但您指的是my_date 既是数字又是字符串;并且显示原始(数字)值,实际转换为的值以及sysdate - 或者最好是systimestamp,因为您正在创建时间戳而不是日期;如果您将其与另一个日期/时间戳进行比较,则根本不需要将其转换为字符串。 我在帖子上添加了更多信息,这是我的值不是字符串,它也是一个字符,也被转储命令批准。你能解释一下你到底是什么意思吗? 【参考方案1】:

你可以像这样简单地运行它:

select my_date
from my_table
where (TIMESTAMP '1970-01-01 00:00:00 UTC' + my_date * INTERVAL '1' SECOND) >= SYSTIMESTAMP - INTERVAL '1' DAY

无需将时间转换为您的当地时间。 TIMESTAMP WITH TIME ZONE 的比较始终在内部按 UTC 时间执行。

为了获得足够的性能,我建议创建一个函数:

CREATE OR REPLACE FUNCTION UnixTime2LocalTime(UnixTime IN NUMBER) RETURN TIMESTAMP DETERMINISTIC IS
BEGIN
    RETURN (TIMESTAMP '1970-01-01 00:00:00 UTC' + UnixTime * INTERVAL '1' SECOND) AT TIME ZONE 'America/New_York';
END UnixTime2LocalTime;
/

然后将虚拟列添加到您的表中

 LOCAL_TIME TIMESTAMP(0)  GENERATED ALWAYS AS ( UnixTime2LocalTime(my_date ) ) VIRTUAL

之后,您还可以在虚拟列上创建索引。

【讨论】:

【参考方案2】:

您不需要将转换后的表格值或当前日期视为字符串。了解转化的各个阶段可能会有所帮助:

-- just for brevity
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF3';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF3 TZR';

create table my_table (my_date)
as select 1580801246921 from dual;

select my_date as epoch,
  DATE '1970-01-01' + (1/24/60/60/1000) * my_date as plain_date,
  CAST(DATE '1970-01-01' + (1/24/60/60/1000) * my_date AS TIMESTAMP) as plain_ts,
  FROM_TZ(CAST(DATE '1970-01-01' + (1/24/60/60/1000) * my_date AS TIMESTAMP), 'UTC') as utc,
  FROM_TZ(CAST(DATE '1970-01-01' + (1/24/60/60/1000) * my_date AS TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York' as est
from my_table;

        EPOCH PLAIN_DATE          PLAIN_TS                UTC                         EST                                     
------------- ------------------- ----------------------- --------------------------- ----------------------------------------
1580801246921 2020-02-04 07:27:27 2020-02-04 07:27:27.000 2020-02-04 07:27:27.000 UTC 2020-02-04 02:27:27.000 AMERICA/NEW_YORK

或者更简单一点,使用时间戳和间隔:

select my_date as epoch,
  TIMESTAMP '1970-01-01 00:00:00' + (my_date/1000) * INTERVAL '1' SECOND as plain_ts,
  FROM_TZ(TIMESTAMP '1970-01-01 00:00:00' + (my_date/1000) * INTERVAL '1' SECOND, 'UTC') as utc,
  FROM_TZ(TIMESTAMP '1970-01-01 00:00:00' + (my_date/1000) * INTERVAL '1' SECOND, 'UTC') AT TIME ZONE 'America/New_York' as est
from my_table;

        EPOCH PLAIN_TS                UTC                         EST                                     
------------- ----------------------- --------------------------- ----------------------------------------
1580801246921 2020-02-04 07:27:26.921 2020-02-04 07:27:26.921 UTC 2020-02-04 02:27:26.921 AMERICA/NEW_YORK

或者更简单地说:

select my_date as epoch,
  TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND as utc,
  (TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND) AT TIME ZONE 'America/New_York' as est
from my_table;

        EPOCH UTC                         EST                                     
------------- --------------------------- ----------------------------------------
1580801246921 2020-02-04 07:27:26.921 UTC 2020-02-04 02:27:26.921 AMERICA/NEW_YORK

这也保留了原始值的小数秒,这可能有用也可能没用(但因为它没有handle leap seconds 精度有点悬而未决......)

然后您可以使用 UTC 值并与 systimestamp 而不是 sysdate 进行比较,因为这也包括时区 - 所以您无需担心转换为本地时间,除了可能用于显示:

select my_date,
  (TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND) AT TIME ZONE 'America/New_York' as est
from my_table
where TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND >= systimestamp - INTERVAL '1' HOUR;

如果您希望将结果作为特定格式的字符串显示 - 而不是让您的客户端/应用程序决定如何格式化它,这就是您现在看到的 - 您可以使用 to_char() 明确控制它:

select TO_CHAR(
    (TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND) AT TIME ZONE 'America/New_York',
    'YYYY-MM-DD HH24:MI:SS') as my_string
from my_table
where TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND >= systimestamp - INTERVAL '1' HOUR;

但是将值保留为时间戳,直到您需要它显示的最后一刻(或其他一些固定输出,例如 JSON) - 不要转换为字符串然后尝试与其他事物进行比较,例如。

【讨论】:

嘿亚历克斯!太感谢了!您的解决方案非常有效!选择 my_date, FROM_TZ(TIMESTAMP '1970-01-01 00:00:00' + (my_date/1000) * INTERVAL '1' SECOND, 'UTC') AT TIME ZONE 'America/New_York' as est from my_table where FROM_TZ( TIMESTAMP '1970-01-01 00:00:00' + (my_date/1000) * INTERVAL '1' SECOND, 'UTC') >= systimestamp - INTERVAL '1' HOUR;您能解释一下如何将格式更改为这样的格式吗? my_date 仅包含 DD-MM-YYYY HH24:MI:SS 例如 04/02/2020 10:52:34 - 就是这样。非常感谢! 查询选择带有时区数据类型值的时间戳。您的客户正在决定如何格式化显示。您可以更改它,但如果您希望查询始终显示相同的格式,则可以将该选定值包装在指定该格式的 TO_CHAR() 调用中。但是 - 只是为了展示;将其保留为时间戳,直到最后一刻,因此您始终在比较正确的数据类型,直到那一刻。 嘿alxe,像这样的吗?选择 my_date 作为纪元,TIMESTAMP '1970-01-01 00:00:00 UTC' + (my_date/1000) * INTERVAL '1' SECOND AT TIME ZONE 'America/New_York' as TO_CHAR(my_date, 'YYYY-MM-DD :HH24:MI:SS') 来自 my_table 否 - 我在答案中添加了一个示例。【参考方案3】:

如果MYDATE 是字符串,则应将其转换为DATE,反之亦然。字符串不是将它们与>< 进行比较的最佳选择。例如,'9' 大于 '20'

where to_date(mydate, 'dd-mm-yyyy hh24:mi:ss') >= sysdate - 1/24

另一方面,如果MYDATE 列中有索引,则TO_DATE 会导致该索引无法使用(除非您选择创建基于函数的索引)。

这就是将日期值存储为字符串时必须付出的代价。你为什么这么做?如果可能,将列的数据类型更改为DATE

【讨论】:

感谢您的回复,我之前尝试过(并再次尝试)它似乎不起作用,它总是给我错误的日期。 不客气。正如我所说,这就是你必须付出的代价。 “错误的日期”是……究竟是什么?您确定所有字符串都以 dd-mm-yyyy hh24:mi:ss 格式存储吗? 是的,我已经用更多信息更新了我的帖子,希望对您有所帮助:)

以上是关于Oracle - 将日期列与 sysdate 进行比较的主要内容,如果未能解决你的问题,请参考以下文章

在 oracle 中比较日期与 sysdate

oracle学习

sysdate 差异(如何在 sysdate 和我的表日期之间进行查询)

特定日期的 Oracle 随机时间戳

在 ORACLE 中从 SYSDATE 中减去日期

Oracle 日期数据类型与 sysdate