为什么count(*)即使使用索引也很慢?

时间:2020-02-17 20:33:44

标签: oracle indexing oracle12c sql-execution-plan sql-tuning

这是我的查询:

   select count(*)
    FROM TB_E2V_DOCUMENTOS_CICLO D
    WHERE (D.TIPOCLIENTE = null or null is null)
      AND (D.TIPODOCUMENTOCLIENTE = null or null is null)
      AND (D.NUMDOCUMENTOCLIENTE = null or null is null)
      AND (D.BA = null or null is null)
      AND (D.FA = null or null is null)
      AND (D.NOMBRECLIENTE = null or null is null)
      AND (D.NUMTELEFONO = null or null is null)
      AND (D.NUMSUSCRIPCION = null or null is null)
      AND (D.TIPORECIBO in ('Recibo'))
      AND (D.NUMRECIBO = null or null is null)
      AND (TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd') BETWEEN TO_DATE('2019-5-1', 'yyyy-MM-dd') AND TO_DATE('2020-2-18', 'yyyy-MM-dd'))
      AND (D.MONTORECIBO = null or null is null)
      AND (D.NUMPAGINAS = 0 or 0 = 0)
      AND (D.NOMBREARCHIVO = null or null is null)
      AND (D.NEGOCIO = null or null is null)
      AND (D.NOMBREMETADATACARGA = null or null is null)
      AND (D.FECHACARGA = TO_DATE(null) or TO_DATE(null) is null);

此查询返回 result

当我对Xplain进行以下操作时:

enter image description here

成本很高,但是此查询使用索引。查询大约持续10秒。 如何提高查询性能?

我正在使用Oracle 12c

2 个答案:

答案 0 :(得分:0)

注意:所有“ and(= null或null为null)”谓词将始终为true; Oracle没有定义空值,因此空值不等于空值,因此,如果要检查空值,请使用“是空值”

select * from dual where null = null; -- returns no rows
select * from dual where not (null <> null); -- returns no rows
select * from dual where null is null;  -- returns 1 row
select * from dual where not(null is not null);  -- returns 1 row

就索引而言,您需要一个选择性索引(即返回更少的行)并且该索引存在于where子句谓词中。在这种情况下,它看起来像是TO_DATE(D.FECHAEMISION,'yyyy / MM / dd')上基于函数的索引 与D.TIPORECIBO一起准备好了。在这种情况下使用INDEX SKIP SCAN可能是因为D.TIPORECIBO不在最前面。索引跳过扫描比索引范围扫描慢,因为它需要读取更多的索引块。

答案 1 :(得分:0)

这里涉及一些因素:

首先,此查询使用的是复合索引的第二(或第三)部分,结果为SKIP SCAN

查看表上的所有索引,看看TIPORECIBO上的索引类型。 这可能不是领先的专栏。您可以通过创建以TIPORECIBO作为前导列的索引来提高性能,但这不太可能-这似乎是“类型”列,可能只有几个值,并且不是索引的理想选择

第二个问题是Oracle使用索引获取一组候选行,然后转到数据块本身以获取行以进行进一步过滤。 如果Oracle不需要获取数据块,则select count(*)的性能会更好。这可以通过创建一个包含过滤器所需的所有数据的索引来实现。

在您的情况下,TIPORECIBOFECHAEMISION上的索引意味着Oracle可以单独访问该索引,而无需访问数据块。

第三个问题是您正在将TO_DATE应用于FECHAEMISION列。如果这是DATE数据类型,则不需要转换,这会给您带来麻烦。如果您确实需要转换,则可以选择在TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd')上使用基于函数的索引。

要调整此特定查询,您可以尝试基于函数的复合索引:

CREATE INDEX TB_E2V_DOCUMENTOS_CICLO_FX1 ON TB_E2V_DOCUMENTOS_CICLO(FECHAEMISION, TO_DATE(D.FECHAEMISION, 'yyyy/MM/dd'))

最后,该查询显然是从代码生成的: 当前端通过AND (D.BA = null or null is null)时,像WHERE这样的行似乎是排除NULL子句部分的一种方式。如果为该参数提供了值,则可能为AND (D.BA = 'X' or 'X' is null)

因此,在调整当前参数集时要小心,因为生成此查询的内容的任何更改都会影响调整的有效性。

如果您有一种方法可以影响此查询的生成方式,则最好在未提供值时排除那些非事件过滤器,尽管Oracle应该能够按原样处理它们。