【问题标题】:Oracle query: date filter gets really slowOracle 查询:日期过滤器变得非常慢
【发布时间】:2020-12-01 21:46:46
【问题描述】:

我有这个 oracle 查询需要大约 1 分钟才能得到结果:

SELECT TRUNC(sysdate - data_ricezione) AS delay
  FROM notifiche@fe_engine2fe_gateway n
 WHERE NVL(n.data_ricezione, TO_DATE('01011900', 'ddmmyyyy')) =
       (SELECT NVL(MAX(n2.data_ricezione), TO_DATE('01011900', 'ddmmyyyy'))
          FROM notifiche@fe_engine2fe_gateway n2
         WHERE n.id_sdi = n2.id_sdi)
--AND sysdate-data_ricezione > 15

基本上我有一个名为“notifiche”的表,其中每条记录代表对另一种对象(发票)的一种更新。我想知道过去 15 天内哪张发票没有收到任何更新。我可以通过加入 notifiche n2 表,获取每张发票的最新记录,并评估更新日期 (data_ricezione) 和当前日期 (sysdate) 之间的差异。

当我添加注释条件时,查询需要无限时间才能完成(我的意思是几个小时,从来没有看到它的结尾......)

这个简单的条件怎么可能让查询变得这么慢?

如何提高性能?

【问题讨论】:

  • 样本数据和期望的结果会有所帮助。这个查询似乎不是特别有用。
  • 该表有 640k 条记录,每张发票平均有 1,1 条记录(因此很难提供数据样本......我有什么可以帮助你的吗?)。期望的结果是了解为什么数据过滤器会使一切变得如此缓慢,以及性能改进(不知何故已经获得)

标签: sql oracle performance


【解决方案1】:

尽量保持data_ricezione单独;如果上面有索引,可能会有所帮助。

所以:切换

and sysdate - data_ricezione > 15

and -data_ricezione > 15 - sysdate / * (-1)

and data_ricezione < sysdate - 15

由于一切都通过数据库链接完成,请查看driving_site 提示是否有任何好处,即

select /*+ driving_site (n) */                --> "n" is table's alias
  trunc(sysdate-data_ricezione) as delay
from
  notifiche@fe_engine2fe_gateway n
...

【讨论】:

  • 我尝试了各种方法:向 data_ricezione 添加索引使事情变得更快。从“and sysdate - data_ricezione > 15”更改为“and data_ricezione
  • 生活大爆炸 :)。性能调优是一个巨大的章节,有关它的书籍都有。但是,以非常非常简单的形式,如果查询运行缓慢 - 请检查您在 WHERE 子句中使用的列是否被索引。如果他们是,它通常会有所帮助。但是,如果在某些表达式中使用了索引列(例如,您使用 TRUNC(date_ricezione)),则将不再使用索引,因此您必须重写条件或创建基于函数的索引。等等。
  • @Littlefoot 绝对正确,但 trunc(indexed_datecol) 不是最好的例子,因为 cbo 知道在某些情况下如何优化 trunc(),例如 cbo 可以在 trunc(indexed_datecol) 的情况下使用索引在范围谓词中
  • 感谢您的提示。您对为什么在日期上添加该条件会使查询如此缓慢有任何解释吗?计算出延迟后,过滤大于15的记录应该是一件轻而易举的事吧?
【解决方案2】:

使用分析函数来避免通过数据库链接进行自联接。下面的查询只从表中读取一次,将行划分为窗口,找到每个窗口的MAX 值,并让您根据该最大值选择行。分析函数一开始很难理解,但它们通常会导致代码更小、更高效。

select id_sdi, data_ricezion
from
(
    select id_sdi, data_ricezion, max(data_ricezion) over (partition by id_sdi) max_date
    from notifiche@fe_engine2fe_gateway
)
where sysdate - max_date > 15;

至于为什么添加一个简单的条件会使查询变慢 - 这完全与基数估计有关。基数,即行数,决定了数据库优化器的大部分决策。加入少量数据的最佳方法可能与加入大量数据的最佳方法大不相同。 Oracle 必须总是猜测一个操作返回了多少行,才能知道使用哪种算法。

优化器统计信息(关于表、列和索引的元数据)是 Oracle 用来进行基数估计的。例如,要猜测sysdate-data_ricezione &gt; 15 过滤掉的行数,优化器会想知道表中有多少行(DBA_TABLES.NUM_ROWS),列的最大值是多少(DBA_TAB_COLUMNS.HIGH_VALUE),以及可能会细分不同年龄范围内的行数 (DBA_TAB_HISTOGRAMS)。

所有这些信息都取决于正确收集的优化器统计信息。如果 DBA 愚蠢地禁用了自动优化器统计信息收集,那么这些问题将一直发生。但即使您的系统使用了良好的设置,您使用的谓词也可能是一个特别困难的情况。优化器统计信息不是免费收集的,因此系统仅在 10% 的数据发生变化时才收集它们。但是由于您的谓词涉及SYSDATE,因此即使表没有更改,行的百分比也会每天更改。比默认计划更频繁地手动收集此表上的统计信息,或使用/*+ dynamic_sampling */ 提示,或创建 SQL 配置文件/计划基线,或管理优化器统计信息和计划稳定性的众多方法之一可能是有意义的。但是,如果您使用分析函数而不是自联接,希望这些都不是必需的。

【讨论】:

    猜你喜欢
    • 2020-11-21
    • 2020-02-06
    • 1970-01-01
    • 2018-05-27
    • 2018-11-26
    • 2019-01-21
    • 1970-01-01
    • 2020-04-04
    • 2014-07-23
    相关资源
    最近更新 更多