这是我的查询:
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);
而且当我为Xplain做:
成本很高,但是此查询使用索引。查询大约持续10秒。如何提高查询性能?
我正在使用Oracle 12c
注意:所有的“和(= 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慢,因为它需要读取更多的索引块。
这里涉及一些因素:
首先,此查询使用复合索引的第二(或第三)部分,结果为SKIP SCAN
。
查看表上的所有索引,查看TIPORECIBO
上的索引类型。这可能不是领先的专栏。您可以通过创建一个以TIPORECIBO
作为前导列的索引来提高性能,但这不太可能-这似乎是“类型”列,可能只有几个值,而不是索引的理想选择。] >
第二个问题是,Oracle使用索引获取一组候选行,然后转到数据块本身以获取行以进行进一步过滤。如果Oracle不需要获取数据块,则select count(*)
的性能会更好。这可以通过创建一个索引来实现,该索引包含过滤器所需的所有数据。
在您的情况下,TIPORECIBO
和FECHAEMISION
上的索引意味着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应该能够按原样处理它们。