【问题标题】:dates, nulls and indexes日期、空值和索引
【发布时间】:2016-06-16 09:01:22
【问题描述】:

我有一张桌子,我可以在其中跟踪事件及其发生日期。 在某些情况下,事件发生日期为空(甚至尚未发生,但已注册)

快速统计:所有事件 390k 行,其中 252k 具有空日期

所以我在根据用户请求提取数据时遇到问题: 1. 用户可能想要尚未发生的拉取事件; (用户输入 *) 2. 用户可以拉取超过特定日期发生的事件; 3. 用户可以拉取超过特定日期发生的事件+尚未发生的事件;

我正在构建动态 sql 查询,类似于

select
  even_id,
  event_registered_date,
  event_name,
  event_occurred_date
from
  events_table
where
  NVL(event_occurred_date, to_date('2033-01-01','yyyy-mm-dd')) >= coalesce(to_date(replace(:p1, '*', NULL),'yyyy-mm-dd'),event_occurred_date,to_date('2033-01-01','yyyy-mm-dd'))
  ...--other filter conditions are here

这个sql最耗费成本的部分是日期过滤器。我尝试创建基于函数的索引trunc(event_occurred_date),甚至包括空值trunc(NVL(event_occurred_date,to_date('2033-01-01','yyyy-mm-dd'))),它仍然使用全表扫描。

我确信有更巧妙的方法可以解决这个问题,但我就是看不到。 提前致谢

添加: 我刚刚与表所有者交谈,他们告诉我,在任何给定时间,至少有一半的事件对于 event_occurred_date 将具有 nulls。也许这将有助于分析 执行计划是:

【问题讨论】:

  • 您为什么不使用默认值设置您的空日期值(例如 2033-01-01 您正在使用 NVL 动态填充)?这似乎是解决您大多数问题的好方法。
  • 另一种解决方案可以将查询分为两部分 - 一个日期为空的部分,这自然会更慢(因为没有明显的过滤器并且它是大部分数据 - 所以索引不是一个选项)和另一个日期不为空的地方,它将使用索引来查询日期特定事件。
  • 不幸的是,我无法影响表格的填充方式,所以我必须按原样工作。关于分成两行,我只能使用一个 sql 查询,而我提供的上部部分是我在更大的选择中使用的 JOIN 之一。所以这也无济于事。谢谢你的建议
  • 有了空值的比例,你的第一个和第三个场景无论如何都不会从索引中受益——全表扫描会更快,因为无论如何你都必须检索这么多的表块。 (除非其他条件可以使用索引,这无论如何都没有实际意义)。第二种情况可能会受益于该列上的简单索引,但如果是这样,单独查询可能会更简单 - 这可行吗?其他条件是否更具选择性和索引列?
  • 关于拆分成两行,我只能使用一个 sql 查询,而我提供的部分上部是我在更大的选择中使用的 JOIN 之一(它们有 PK 索引,它们的成本是最小(小于 3)。

标签: sql oracle performance


【解决方案1】:

首先在建立索引之后 - 你分析过索引吗?

假设您有 - 您没有提及您正在运行的查询类型。 如果查询参数正在搜索 NULL - 我不会对优化器选择使用全表扫描而不是索引范围扫描感到惊讶。 毕竟,您的大部分记录集都有一个 NULL 日期值。

您也可以使用提示来强制使用索引 IE。 /*+ 索引(events_table_idx) */

但您需要仔细查看性能统计数据以确定哪种方式是检索数据的最有效方式。

【讨论】:

  • 结果 sql 选择中没有 where event_occurred_date is NULL 部分。它应该符合一般模式:当用户输入 * - 然后我应该跳过检查 event_occurred_date (意味着,无论是否发生日期,包括空值),当用户输入日期时,它应该检查输入的日期。是的,我尝试指定索引提示,它仍然使用全表扫描,我认为这确实比对索引进行范围扫描要快。
猜你喜欢
  • 1970-01-01
  • 2011-03-05
  • 1970-01-01
  • 2018-10-29
  • 2021-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-16
相关资源
最近更新 更多