有一个表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
下面是解释计划
答案 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个并集,并检查其运行速度是否更快,但是查询可能变得太复杂而无法解析。因此,即使您尝试手动执行此转换,也可能不可行,除非您发现一些技巧,例如在循环中运行数百个简单查询并将中间结果转储到临时表中。