为什么使用索引,但是sql仍然很慢

时间:2019-08-16 04:08:35

标签: sql oracle11g

有一个表ORG_HLD_INFO及其索引:"ORG_HLD_INFO" ("HLD_UNI_CODE", "ISVALID", "ORG_UNI_CODE")。现在执行下面的查询很慢,需要3.26秒(并且提取的所有行都是466)

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE in (30004536568,...) 

为什么这么慢?它应该使用索引以及索引中的所有select字段,因此无需查询表行。

p.s。 该表的总数为:109102083 下面是解释计划

enter image description here

1 个答案:

答案 0 :(得分:1)

您在评论中说,您的IN子句可能有大约100个值。

在这种情况下,索引通常是无用的。

让我们看一下查询的简化版本

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE in (3, 4, 5) 

这等效于

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and 
    (HLD_UNI_CODE = 3 OR HLD_UNI_CODE = 4 OR HLD_UNI_CODE = 5) 

这等效于

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 3

UNION ALL

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 4

UNION ALL

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 5

在这里我们可以使用UNION ALL,因为我们在选定的列中包括了HLD_UNI_CODE。如果不是,我们可能需要使用UNION

无论如何,重点是像

这样的每个简单查询
select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 5

可以使用索引。从某种意义上说,就是引擎将在索引中查找仅查找所需的行,而不是扫描所有行。

在某些数据库中,优化器可能足够聪明,可以将IN / OR查询重写为更简单的索引查找UNION(如果{{1中只有很少的值}})。我不知道Oracle的优化器是否完全可以进行这种转换。

但是,当您有成百上千个这样的简单查询时,执行所有这些搜索然后将它们组合在一起很快变得太昂贵了,因此优化器选择扫描整个表。扫描表(或索引)仍然意味着读取所有109102083行并将过滤器应用于每一行。

您可以在计划IN中看到而不是查找,这实际上意味着读取所有行。而且您会看到谓词(过滤器)带有一堆RANGE SCAN

您可以尝试将查询重写为100个并集,并检查其运行速度是否更快,但是查询可能变得太复杂而无法解析。因此,即使您尝试手动执行此转换,也可能不可行,除非您发现一些技巧,例如在循环中运行数百个简单查询并将中间结果转储到临时表中。