【发布时间】:2021-06-25 18:11:53
【问题描述】:
互联网,请帮忙!
我使用的是 Oracle DB v 12c,其中发票开具时间存储为 NUMBER 类型列中的 unix 时间戳 + 还有一个 VARCHAR2(128) 列,它定义了开具发票的时区,例如“欧洲/伦敦”。
所以在本地日期“2020-10-10”开具的发票可以这样查询:
SELECT inv.invoiceID
FROM invoices inv
WHERE TO_CHAR(
FROM_TZ(timestamp '1970-01-01 00:00:00'
+ NUMTODSINTERVAL(inv.INVOICETIME, 'SECOND'), 'UTC') AT TIME ZONE inv.timezoneName,
'YYYY-MM-DD') = '2020-10-10';
但不幸的是,此查询不可索引。
问题是,这不起作用:
CREATE INDEX Invoices_InvoiceDate ON Invoices (
TO_CHAR(
FROM_TZ(timestamp '1970-01-01 00:00:00'
+ NUMTODSINTERVAL(INVOICETIME, 'SECOND'), 'UTC') AT TIME ZONE timezoneName,
'YYYY-MM-DD')
);
错误: ORA-01743: 只能索引纯函数
由于使用了“AT TIME ZONE”,这个算子的“纯版”是什么?或者换句话说 - 如何在 Oracle 中将 Unix 时间戳属性转换为本地日期?
【问题讨论】:
-
以您目前收集和存储数据的方式,我不相信该函数有任何“纯版本”,或者“正确的方式将”从 UTC 转换为“本地”日期(发票开具的“本地”,即;请注意,您对该术语的非标准使用会使读者感到困惑,因为“本地”一词通常表示“数据库会话本地”,而不是“时区本地”保存在表格中”)。
-
所以:没有“纯函数”,因为要转换日期,Oracle 必须查阅时区偏移表。如果一个函数必须在表中查找,它会自动被认为是不确定的:表中的数据可能会随着时间而改变,因此该函数不会总是产生相同的结果。你和我可能相信这个特定的查找表永远不会改变,但是查询解析器并没有那么详细;该函数使用表查找,因此它是不确定的、不可索引的周期。因此,如果您必须有索引,则需要找到一种不同的方式来存储输入数据。
-
感谢@mathguy 确认在这种情况下,由于时区查找表,基于函数的索引是不可能的。所以我可能会通过将问题日期存储在单独的列中来解决它。v
-
对我来说,不清楚您在寻找什么。将日期/时间值与 strings 进行比较通常不是明智的选择。并且在没有任何时间的情况下对日期值使用时区也没有多大意义——至少它会让你更难理解你的意图。我假设您实际上正在寻找这个
WHERE TIMESTAMP '1970-01-01 00:00:00 UTC' + NUMTODSINTERVAL(INVOICETIME, 'SECOND') = FROM_TZ(TIMESTAMP '2020-10-10 00:00:00', timezoneName)。
标签: oracle timezone unix-timestamp