提问人:Binomi 提问时间:11/15/2023 最后编辑:Paul WBinomi 更新时间:11/16/2023 访问量:86
SQL在硬编码时使用索引,但在使用子选择时不使用
SQL using Index when hardcoded, but not when using a subselect
问:
我想选择表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 非常小。
连接的执行计划:
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
答:
问题在于您正在使用,这会导致优化器可能无法有效地使用索引。因为它在某种程度上类似于拥有一个与第一个表无关的临时表。subquery
为了提高性能,您需要使用直接连接,例如:
SELECT t.*
FROM table t
JOIN t2 ON t.name = t2.name2
WHERE date = xyz AND status = 1;
另外,请确保您有 fk,这有助于您的加入变得更快。
评论
尝试使用一些提示:
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该索引的实际名称。
评论
您想要选择与名称列表匹配的行。在极端情况下,可能会发生没有一行与任何名称匹配或所有行都匹配的情况。
在第一个查询中,优化程序确切地知道要查找的名称数量和名称。这有助于它决定预期的匹配数量。此外,您使用的是子句,因此即使列表中有重复值,主表的行也只会被选中一次。优化器认为这是使用索引执行此操作的最快方法。IN
在第二个查询中,您更改了两个内容:
- 您已将硬编码值替换为 select。DBMS 现在必须知道 t2 表,以便估计预期的匹配项数。桌子上有统计数据吗?它们是最新的吗?只运行整个表而不是使用索引听起来像是获得稳定运行时间的好方法。
- 您已将子句替换为联接。这意味着,从理论上讲,您可以在结果中获得 t 行数倍。只有当 t2.name2 上存在唯一键时,DBMS 才会看到它保证不会有任何重复项。
IN
我不知道你为什么切换到联接。您需要加入吗?有没有你想从 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 的行百分比是多少?百分比越小,色谱柱的选择性越强。将最具选择性的列设为索引中的第一个列。
评论
DBMS_STATS.GATHER_TABLE_STATS
method_opt
评论
date
status
name
date
status
name
name
t2
t1