oracle SQL在硬编码时使用索引,但在使用子选择时不使用索引

2w3kk1z5  于 2023-11-17  发布在  Oracle
关注(0)|答案(3)|浏览(81)

我想选择一个表t中的所有数据,它有一个特定的名称作为列条目。表t有一个关于列名的索引。
当我在where子句中执行指定硬编码的名称时,就会使用索引,并且计算结果的速度非常快。
但是,当我使用与另一个(小)表的连接时,索引不会被使用,并且它非常慢。
硬编码:

SELECT t.*
FROM table t
WHERE date = xyz
AND name in ('hardcoded_name_1','hardcoded_name_2', ...)
AND status = 1;

字符串
加入我真的想做的事:

SELECT t.*
FROM table t
JOIN (SELECT name2 from t2) on name = name2
WHERE date = xyz
AND status = 1;


这里,表t是大的,表t2非常小。
join的执行计划:

SELECT STATEMENT ALL_ROWS 
  4 NESTED LOOPS 
    2 PARTITION RANGE ALL 
      1 TABLE ACCESS STORAGE FULL TABLE t.name 
  3 INDEX UNIQUE SCAN INDEX (UNIQUE) t2.name

qcbq4gxm

qcbq4gxm1#

您希望选择与名称列表匹配的行。在极端情况下,可能会出现没有行与任何名称匹配或所有行都匹配的情况。
在第一个查询中,优化器准确地知道要查找多少个名称以及哪些名称。这有助于它决定期望多少个匹配。此外,您正在使用IN子句,因此即使列表中有重复的值,主表的行也只会被选择一次。优化器认为使用索引是最快的方法。
在第二个查询中,您更改了两件事:
1.你已经用一个select替换了硬编码的值。DBMS现在必须知道t2表,以便估计期望有多少匹配。表上有统计信息吗?它们是最新的吗?只是运行整个表而不是使用索引听起来像是一个获得稳定运行时间的好方法。
1.你已经用一个join替换了IN子句。这意味着,理论上你可以在结果中得到t行的多个行。只有当t2.name2上有一个唯一的键时,DBMS才能保证没有任何重复。
我不知道你为什么切换到一个连接。你需要一个连接吗?有没有你想要从t2中选择的数据?看起来,你仍然只需要查找:

SELECT t.*
FROM table t
WHERE date = xyz
AND name in (SELECT name2 from t2)
AND status = 1;

字符串
然后,您可能希望再次收集两个表的统计信息,理想情况下,在名称列上使用直方图,以便DBMS充分了解您的表,从而获得最佳执行计划。
最适合查询的索引似乎是这样的

create index idx1 on t (date, name, status);


或者也许

create index idx2 on t (name, status, date);


或其他具有这三个列的索引。索引应该按其选择性顺序排列列。对于一个名称,您获得的行的百分比是多少?对于一个日期,您获得的行的百分比是多少?对于状态1,您获得的行的百分比是多少?百分比越小,列的选择性越强。将最具选择性的列设置为索引中的第一列。

irtuqstp

irtuqstp2#

尝试使用一些提示:

SELECT /*+ leading(t2) use_nl(t) index(t ix_t_name) */ t.*
FROM table t
JOIN t2 on name = name2
WHERE date = xyz AND status = 1

字符串
;
其中ix_t_name是该索引的实际名称。

2sbarzqh

2sbarzqh3#

问题是你正在使用subquery,这会导致优化器可能无法有效地使用索引。因为它在某种程度上类似于拥有一个与第一个表没有关系的临时表。
为了提高性能,您需要使用直接连接,例如:

SELECT t.*
FROM table t
JOIN t2 ON t.name = t2.name2
WHERE date = xyz AND status = 1;

字符串
此外,确保你有fk,这有助于你的加入变得更快。

相关问题