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

问题描述 投票:0回答:2

这是我的查询:

   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

oracle indexing oracle12c sql-execution-plan sql-tuning
2个回答
0
投票

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

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不在最前面。 INDEX SKIP SCAN比INDEX RANGE SCAN慢,因为它需要读取更多的索引块。


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应该能够按原样处理它们。

© www.soinside.com 2019 - 2024. All rights reserved.