Oracle查询性能优化求助:现有查询耗时久且索引无效
Oracle查询调优方案
先聊聊你的查询可能存在的性能瓶颈:
NOT IN子查询在处理大量数据时,Oracle优化器的执行效率往往不如NOT EXISTS,而且如果子查询返回的col_11存在NULL值,NOT IN会直接返回空结果,这可能不是你预期的;- 两个
OR的LIKE条件,虽然是前缀匹配(可以用到B树索引),但OR可能会让优化器选择全表扫描,或者做索引合并,效率不如范围查询; - 如果
table2没有针对id和col_11的联合索引,子查询的过滤会很慢。
下面给你几个具体的优化改写方案:
方案1:用NOT EXISTS替换NOT IN,并优化LIKE为范围查询
这是最常用的优化方式,改写后的查询:
SELECT t1.* -- 建议替换成具体需要的列,比如 col_1, col2, ... FROM table1 t1 WHERE t1.col_1 >= '8' AND t1.col_1 < ':' -- ASCII码中'9'的下一个字符是':',这样覆盖所有以8/9开头的字符串 AND NOT EXISTS ( SELECT 1 FROM table2 t2 WHERE t2.id = 2 AND t2.col_11 >= '8' AND t2.col_11 < ':' AND t2.col_11 = t1.col_1 );
为什么这么改:
NOT EXISTS是半连接,Oracle会在找到匹配的记录后立即停止扫描,比NOT IN的全量匹配更高效;- 把
col_1 like '8%' OR col_1 like '9%'改成范围查询col_1 >= '8' AND col_1 < ':',可以让优化器直接走col_1的B树索引进行范围扫描,避免OR带来的索引合并或全表扫描开销。
方案2:添加针对性索引
索引是性能提升的关键,建议创建以下索引:
- 给
table1的col_1建普通B树索引:
CREATE INDEX idx_table1_col1 ON table1(col_1);
如果你的查询只需要特定列,可以创建覆盖索引,进一步减少回表开销:
CREATE INDEX idx_table1_col1_covering ON table1(col_1) INCLUDE (col2, col3); -- 替换成你需要的其他列
- 给
table2建id + col_11的联合索引:
CREATE INDEX idx_table2_id_col11 ON table2(id, col_11);
这个索引可以让子查询快速定位id=2的行,同时直接过滤col_11的范围条件,无需回表。
方案3:用LEFT JOIN + IS NULL替代NOT IN
如果你更喜欢用JOIN的写法,也可以这样改写:
SELECT t1.* -- 同样建议替换成具体列 FROM table1 t1 LEFT JOIN table2 t2 ON t2.id = 2 AND t2.col_11 >= '8' AND t2.col_11 < ':' AND t2.col_11 = t1.col_1 WHERE t1.col_1 >= '8' AND t1.col_1 < ':' AND t2.col_11 IS NULL;
这种写法和NOT EXISTS的执行计划通常类似,优化器会根据数据量自动选择最优方式。
额外建议
- 永远避免
SELECT *,只查询你实际需要的列,减少磁盘I/O和内存占用; - 可以用
EXPLAIN PLAN查看原查询和改写后查询的执行计划,对比索引的使用情况(比如是否有INDEX RANGE SCAN而不是FULL TABLE SCAN); - 如果
table2中id=2的记录很多,可以考虑提前将子查询的结果缓存到临时表,再和table1关联,但一般情况下前三个方案已经足够。
内容的提问来源于stack exchange,提问作者Suyash Gupta




