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 进行比较的主要内容,如果未能解决你的问题,请参考以下文章