Oracle:有效地使用where子句过滤时间戳列以获取特定日期的所有记录
Posted
技术标签:
【中文标题】Oracle:有效地使用where子句过滤时间戳列以获取特定日期的所有记录【英文标题】:Oracle: efficiently where clause to filter timestamp column to get all records of a specific day 【发布时间】:2020-03-09 12:38:24 【问题描述】:我在 Oracle 中有一个按时间戳列划分的每月分区表。该表包含来自 2019 年历史数据的超过 10 亿行。现在我想过滤此表以获取特定日期的所有结果,关于 HH24:MI:SS
部分。
我面临的问题 (#1) 是,当使用 TO_CHAR(TIMESTAMPCOLUMN, 'YYYY-MM-DD')
时,我的查询的运行时间在最近几个月增加了。示例:
SELECT * FROM BIG_PART_TABLE WHERE TO_CHAR(TIMESTAMPCOLUMN, 'YYYY-MM-DD') = '2019-01-01' -- 3 sec
SELECT * FROM BIG_PART_TABLE WHERE TO_CHAR(TIMESTAMPCOLUMN, 'YYYY-MM-DD') = '2019-02-01' -- 6 sec
SELECT * FROM BIG_PART_TABLE WHERE TO_CHAR(TIMESTAMPCOLUMN, 'YYYY-MM-DD') = '2019-12-01' -- 36 sec
所以我摆脱了TO_CHAR
并开始像这样过滤:
SELECT * FROM BIG_PART_TABLE WHERE TIMESTAMPCOLUMN BETWEEN DATE '2019-01-01' AND DATE '2019-01-02' -- 0.032 sec
SELECT * FROM BIG_PART_TABLE WHERE TIMESTAMPCOLUMN BETWEEN DATE '2019-12-01' AND DATE '2019-12-02' -- 0.031 sec
问题 (#2) 是我懒得写 BETWEEN
子句,除了它增加了出错的机会。
最后,我真正想要的是一个高效的单个 where 子句来过滤我的表,例如:
SELECT * FROM BIG_PART_TABLE WHERE TIMESTAMPCOLUMN = DATE '2019-01-01'
谢谢大家。
【问题讨论】:
您需要在表中添加一个日期列并将其用于分区。 【参考方案1】:正确的方法是不要在日期列上使用日期函数 - 使用这样的函数会使查询成为非 SARGable,这意味着它不能利用日期列上的索引。
没有语法糖可以使表达式更短。
我还建议使用半开间隔而不是between
:
WHERE
TIMESTAMPCOLUMN >= DATE '2019-01-01'
AND TIMESTAMPCOLUMN < DATE '2019-01-02'
BETWEEN
包含两端,因此您的表达式暗示2019-01-02 00:00:00
上的时间戳将被过滤掉,而这很可能不是您想要的。
【讨论】:
我认为这不能回答 OP 的问题。两个比较似乎比between
更复杂。
非常注意 BETWEEN
关键字中的 2019-01-02 00:00:00
。
@GordonLinoff 您可以写成WHERE TIMESTAMPCOLUMN BETWEEN TIMESTAMP '2019-01-01 00:00:00' AND TIMESTAMP '2019-01-01 23:59:59.999999999'
,但根据 GMB 的回答,在上限不包含在内的情况下,仅使用日期比较可能会更容易。【参考方案2】:
使用 partition_extension_clause 语法:
SELECT *
FROM BIG_PART_TABLE PARTITION FOR (DATE '2019-12-01')
WHERE TRUNC(TIMESTAMPCOLUMN) = DATE '2019-12-01' ;
这段代码还是有点乱。但至少这种语法允许您使用相同的日期文字,而不必创建全新的日期表达式。虽然代码有重复,但重复有点自证:第一个表达式是使用分区修剪来找到最近的段,第二个表达式是得到确切的行。
【讨论】:
【参考方案3】:为了使用分区,Oracle 必须识别分区键。如果它使用完整的时间戳,那么您可能会遇到问题。
它很有可能使用trunc(TIMESTAMPCOLUMN)
或trunc(TIMESTAMPCOLUMN, 'DD')
。如果是这样,那么您可以使用它
WHERE TRUNC(TIMESTAMPCOLUMN) = DATE '2019-01-01'
一旦你弄清楚了,你就可以在表中添加一个计算列,这样你就有了:
alter table big_part_table add column timestampcolumn_date as trunc(timestampcolumn);
那么你可以在WHERE
子句中使用timestampcolumn_date
。
【讨论】:
感谢您的回答戈登。TRUNC
函数导致与 TO_CHAR
函数相同的性能问题。 alter table
让我在单词 AS
中出现语法错误。除此之外,我认为您的想法是拥有一个仅日期列,然后使用该列将表拆分为分区是一个好主意,而且很简单。但是,我需要重写 ETL 的某些部分。
@DanielBonetti 。 . .关键是用于定义分区的方法是您可以在where
子句中安全使用的方法。【参考方案4】:
在 Orace 中访问数据的最快方法是使用分区名称。
就像这个例子:
select * from BIG_PART_TABLE partition(ParititonName);
【讨论】:
以上是关于Oracle:有效地使用where子句过滤时间戳列以获取特定日期的所有记录的主要内容,如果未能解决你的问题,请参考以下文章