【问题标题】:Why does checking for null slow this query down?为什么检查 null 会减慢此查询的速度?
【发布时间】:2016-12-04 02:38:57
【问题描述】:

我得到了这个包含 7,000 条记录的表

desc ARADMIN.V_PKGXMLCODE

Name                  Null     Type          
--------------------- -------- ------------- 
REQUEST_ID            NOT NULL VARCHAR2(15)  
AVAILABILITY                   VARCHAR2(69)  
XML_CODE                       CLOB          
PACKAGENAME_UNIQUE             VARCHAR2(50)  
CATALOG                        NUMBER(15)    
CHILD                          VARCHAR2(255) 
CLASSIFICATION_SYSTEM          NUMBER(15)    
E_MAIL                         VARCHAR2(69)

查询

SELECT COUNT(*) FROM ARADMIN.V_PKGXMLCODE WHERE (CATALOG <> 0 AND CATALOG <> 2) AND (NOT (CHILD IS NULL));

不到一秒。

查询

SELECT COUNT(*) FROM ARADMIN.V_PKGXMLCODE WHERE (CATALOG IS NULL OR (CATALOG <> 0 AND CATALOG <> 2)) AND (NOT (CHILD IS NULL));

需要 23 秒。

解释计划但声称它应该很快......

我能做什么?

【问题讨论】:

  • 第一个查询是否也进行全表扫描,或者它可以使用 field4 上的索引?如果有,表是否被重复删除(未截断)并使用直接路径插入重新填充(即使用/*+ append */ 提示)?
  • 第一次查询的计划怎么样?
  • IS NULL 条件将忽略索引,因为您无法索引 NULL 值。好吧,您当然可以通过在索引时使用常量值和 NULLABLE 列来欺骗优化器。但是,要在您的问题范围内回答,您的第二个查询将进行 FULL TABLE SCAN 除非您像我之前所说的那样欺骗优化器。您的第一个查询可以利用索引并避免 FTS。 1.也请发布第一次查询的解释计划。 2. 统计数据是最新的吗?
  • 在我提供的评论和答案中(现已删除,因为它们都是错误的),我提出了两种可能性:显示结果需要很长时间 - 亚历克斯普尔正确指出这不能成为问题,因为 OP 选择 COUNT(*),而不是完整的行集。我认为问题可能出在网络上——但亚历克斯再次正确地指出,这会减慢两个查询,而不仅仅是一个。想在这里记录一下,因为我删除了我的评论和答案。
  • 如果这是一个视图而不是一个表,那么几乎不可能回答你的问题。这完全取决于视图的作用。如果它是一个 TABLE,我会说 Oracle 不可能需要 27 秒来完成 7,000 行的全表扫描并执行一些布尔运算,除非你可能在 Raspberry Pi 上运行它。哪怕一秒钟似乎也很多。我能想出的唯一原因是下面 Alex 指出的高水印问题。

标签: sql oracle performance oracle11g


【解决方案1】:

这很有趣。我希望查询具有相同的性能,因为 Oracle 有一个很好的优化器,不应该被 NULL 混淆。

这个版本如何有更好的性能?

select x1.cnt + x2.cnt + x3.cnt
from (select count(*) as cnt
      from MYTABLE
      where field4 = 1 and child is not null
     ) x1 cross join
     (select count(*) as cnt
      from MYTABLE
      where field4 = 4 and child is not null
     ) x2 cross join
     (select count(*) as cnt
      from MYTABLE
      where field4 is null and child is not null
     ) x3;

此版本应该能够利用MYTABLE(field4, child) 上的索引。

【讨论】:

  • 什么是 x1 x2 x3?它说 ORA-00904: "X3": invalid identifier?
  • @Thomas - 它们是内联视图的表别名;他们应该是x3.cnt等等。
  • @Thomas。 . .那是与建议的索引?
  • 非常抱歉戈登。我搞砸了原始查询。它实际上看起来像这样 SELECT COUNT(*) FROM ARADMIN.V_PKGXMLCODE WHERE (CATALOG IS NULL OR (CATALOG 0 AND CATALOG 2)) AND (NOT (CHILD IS NULL));我们有一个关于 Catalog 和 Child 的复合索引。
【解决方案2】:

我认为获得这种执行速度差异的唯一方法是 (a) 在 field4 上有一个索引,并且 (b) 有 lot 的空数据块;可能来自重复的直接路径负载设置的非常高的高水位线。

第一个查询仍将使用索引并按预期执行。但是由于空值没有被索引,索引不能用于检查or field4 is null条件,所以它会退回到全表扫描。

这本身不应该是一个问题,因为对 7000 行的全表扫描应该不会花费很长时间。但由于它 花费了这么长时间,因此正在发生其他事情。全表扫描必须检查分配给表的每个数据块以查看它们是否包含任何行,并且它所花费的时间表明存在比您需要容纳 7000 行更多的块,即使使用内联 CLOB 存储也是如此。

获取大量空数据块的最简单方法是拥有大量数据,然后删除大部分数据。但我相信你在一个现已删除的评论中对先前的问题说性能过去还可以,但现在变得更糟了。如果您执行direct-path inserts,则可能会发生这种情况,特别是如果您通过删除数据然后以直接路径模式插入新数据来“刷新”数据。您可以使用具有/*+ append */ 提示的插入来执行此操作;或并行;或通过 SQL*Loader。每次你这样做时,高水位线都会移动,因为旧的空块不会被重复使用;并且每次检查空值的查询的性能都会降低一点。经过大量的迭代之后,真正开始累积起来。

您可以检查数据字典以查看为您的表分配了多少空间(user_segments 等),并将其与您认为实际拥有的数据大小进行比较。您可以通过重建表来重置 HWM,例如:

alter table mytable move;

(最好在维护窗口中!)

作为一个演示,我运行了一个循环来直接路径插入和删除 7000 行超过一百次,然后运行您的两个查询。第一个耗时 0.06 秒(其中大部分是 SQL Devleoper 开销);第二个是 1.260。 (我也跑了 Gordon's,它的时间差不多,因为它仍然需要做 FTS)。随着更多的迭代,差异会变得更加明显,但我的空间不足......然后我做了一个alter table move 并重新运行了您的第二个查询,然后花了 0.05 秒。

【讨论】:

  • 所以你建议我改变表 ARADMIN.V_PCKXMLCODE 移动; ?
  • 或者以其他方式重建它——但你可能不应该这样做,而人们可能正在访问它。我正在尝试考虑一个查询,该查询将首先表明这绝对是问题所在,而且我没有比查看user_segments 更好的方法 - 一个真正的 DBA 可能会提出一个想法。您还需要确定是否实际发生了直接路径负载;如果是这样,问题将逐渐回来。表中的数据是如何维护的?从它的名字看……它实际上是一个视图吗?嗯,是的,计划说实际表是 T111 - 那么 那个 是如何维护的?
  • 我继续在视图正在访问的表上运行查询。结果完全一样。试图找出数据是如何维护的。
【解决方案3】:

我实际上正在为类似的问题而苦苦挣扎。我有一个条件需要从查询中过滤掉所有 NULL 值。

我开始:

ColumnName IS NOT NULL

这增加了我的查询时间,在此之后我尝试了很多东西,比如我只返回我需要的东西的函数,尽管效果不佳。最后,一个小小的改变就奏效了,我所做的是:

IsNull(ColumnName,'') <> ''

它有效,我不完全确定有什么区别,尽管它有效。

【讨论】:

    【解决方案4】:

    IS NULL 不适用于计数。您收到错误“调用本机函数 'ISNULL' 时参数计数不正确”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-22
      • 2011-05-27
      • 2011-10-04
      • 2017-12-24
      相关资源
      最近更新 更多