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

为什么使用索引,但sql仍然很慢,sql,oracle11g,Sql,Oracle11g,有一个表ORG\u HLD\u INFO和它的索引:“ORG\u HLD\u INFO”(“HLD\u UNI\u code”、“ISVALID”、“ORG\u UNI\u code”)。现在执行下面的查询速度很慢,需要3.26秒(并且获取的所有行都是466行) 为什么这么慢?它应该使用索引,以及索引中select的所有字段,因此无需查询表行 p、 美国。 该表的总计数为:109102083 下面是解释计划 您在评论中说,您的in子句可能有大约100个值 在这种情况下,索引通常是无用的 让我们看

有一个表
ORG\u HLD\u INFO
和它的索引:
“ORG\u HLD\u INFO”(“HLD\u UNI\u code”、“ISVALID”、“ORG\u UNI\u code”)
。现在执行下面的查询速度很慢,需要3.26秒(并且获取的所有行都是466行)

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

p、 美国。 该表的总计数为:
109102083
下面是解释计划


您在评论中说,您的
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
这相当于

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\u UNI\u code
。如果不是,我们可能需要使用
UNION

无论如何,关键是每个单独的简单查询

select HLD_UNI_CODE ,ORG_UNI_CODE
from ORG_HLD_INFO
where ISVALID=1 and HLD_UNI_CODE = 5
可以使用索引。使用,从某种意义上说,引擎将在索引中查找以仅查找所需的行,而不是扫描所有行

在某些数据库中,优化器可能足够聪明,可以将/
中的
查询重写为更简单索引搜索的
联合(如果
中的
中只有很少的值)。我不知道Oracle的优化器是否能够完成这种转换

但是,当您有数百个这样简单的查询时,执行所有这些查找并将它们放在一起的成本很快就会变得太高,因此优化器选择扫描整个表;扫描表(或索引)仍然意味着读取所有109102083行,并对每一行应用过滤器

您可以在plan
范围扫描中看到,而不是seek,这实际上意味着读取所有行。您可以看到谓词(filter)带有一堆
s


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

在(30004536568,…)
子句中的
中有多少值?如果它不止几个,那么索引就毫无用处。这是一个综合索引吗?@VladimirBaranov谢谢!通常在100左右,但是为什么在这种情况下索引是无用的,如何改进它呢?您可以提供执行的SQL监视器报告吗。在你知道发生了什么之前,一切都只是猜测…@BobC添加了解释planThanks!全局分区索引在这种情况下有用吗?@朱国伟,我没有分区索引的经验,但我怀疑它们是否有用。如果可以将
IN
子句中的
值列表放入表中,则可以将查询重写为“强制”优化器执行搜索。我尝试使用的关键思想是所谓的横向连接。我对Oracle了解不多,但它应该支持它。不过,我不知道Oracle的确切语法。你最好再问一个问题。不过,不能保证它会比全扫描快。