我有一个不使用索引的查询。有人能说出原因吗?
explain plan set statement_id = 'bad8' for
select
g1.g1id,a.a1id from atable a,
(
select
phone,address,g1id from gtable g
where
g.active = 0 and
(g.name is not null) AND
(SYSDATE - g.CTIME <= 2*365)
) g1
where
(
(a.phone.ph1 = g1.phone.ph1 and
a.phone.ph2 = g1.phone.ph2 and
a.phone.ph3 = g1.phone.ph3
)
OR
(a.address.ad1 = g1.address.ad1 and a.address.ad2 = g1.address.ad2)
)
在两个表中:atable,gtable我有这些索引:
1.在phone.ph1上,phone.ph2,phone.ph3
2.在address.ad1上,address.ad2
电话,地址是自定义数据类型
使用Oracle 11g。
以下是解释计划查询和输出:
SELECT cardinality "Rows",
lpad(' ',level-1)||operation||' '||
options||' '||object_name "Plan"
FROM PLAN_TABLE
CONNECT BY prior id = parent_id
AND prior statement_id = statement_id
START WITH id = 0
AND statement_id = 'bad8'
ORDER BY id;
结果:
> Rows Plan
490191190 SELECT STATEMENT
> null CONCATENATION
> 490190502 HASH JOIN
> 511841 TABLE ACCESS FULL gtable
> 41332965 PARTITION LIST ALL
> 41332965 TABLE ACCESS FULL atable
> 688 HASH JOIN
> 376893 TABLE ACCESS FULL gtable
> 41332965 PARTITION LIST ALL
> 41332965 TABLE ACCESS FULL atable
无论是atable,gtable都有超过1000万行 列电话和地址中的大多数值都不重复。
答案 0 :(得分:3)
Oracle选择的索引取决于许多因素,包括您在问题中未提及的内容,例如表中的行数,列中值的频率以及当多个列时是否有单独或组合索引被编入索引。
话虽如此,我想你的指数没有被使用的主要原因是:
您不直接加入GTABLE / GLOBAL。相反,你加入一个视图,该视图有三个不属于索引的WHERE子句,因此在这个星座中效果较差。
JOIN条件包含OR,这使得难以使用索引。
<强>更新强>
如果Oracle使用你的索引来进行连接 - 由于OR条件已经非常困难 - 它最终将会有大量的ROWID。对于每个ROWID,它必须获取整行。由于全表扫描很容易比ROWID提取快50倍(我不知道Oracle使用什么值),因此如果它可靠地知道连接会减少行数,它将只使用索引。获取50倍。
在您的情况下,剩余的WHERE条件(g.active = 0,g.name不为空,SYSDATE - g.CTIME&lt; = 2 * 365),这些条件未在索引中表示。所以他们必须在连接之后和提取GTABLE行之后应用。这使得比全表扫描更难以达到50倍的结果集。
所以我非常确定Oracle的成本估算是正确的,即使用索引会导致更昂贵的查询,甚至更长的执行时间。
答案 1 :(得分:1)
我们可以说“您的查询不使用索引,因为它们不需要它们”。散列连接更好。要使用索引,oracle需要对它们进行全面扫描(4个索引),进行两次连接,创建一个rowid,然后从表中读取可能需要很多块。如果他认为结果有很多行,那么CBO会进行全扫描,因为速度更快。
没有条件可以减少从表中获取的行数。没有范围扫描。它必须进行全面扫描。