You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL执行计划优化问题:强制索引仍无法避免TABLE ACCESS FULL

Oracle查询强制索引提示无效,始终出现全表扫描的问题

我遇到一个棘手的问题:想在查询执行计划里避免TABLE ACCESS FULL,但即使加了/* index( ) */强制索引提示也完全没效果。

我的查询语句如下:

SELECT af.ID, af.nom_flux, st.chemin_stockage, af.hash_flux 
FROM stockage st 
INNER JOIN allotissement_flux af ON EXISTS (
    SELECT * FROM signature sig 
    WHERE st.id_flux = sig.id_flux 
    AND af.ID = sig.id_flux 
    AND sig.statut_signature = 'SIGNE' 
    AND sig.nb_appel_service_signature < 4 
    AND sig.date_statut_signature >= sysdate - 1000
) 
WHERE st.statut_stockage = 'OUI' 
AND st.date_statut_stockage >= sysdate - 1000

我已经给每个表的每个字段都单独建了索引,但当前执行计划还是全表扫描:

Plan hash value: 2782848463
---------------------------------------------------------------------------------------------------
| Id | Operation          | Name               | Rows | Bytes |TempSpc| Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------------
| 0  | SELECT STATEMENT   |                    | 40M  | 8376M |       | 1594K (1)  | 00:01:03 |
|* 1 | HASH JOIN          |                    | 40M  | 8376M | 4284M | 1594K (1)  | 00:01:03 |
|* 2 | HASH JOIN          |                    | 40M  | 3821M | 1505M | 543K (1)   | 00:00:22 |
| 3  | SORT UNIQUE        |                    | 40M  | 1042M |       | 146K (1)   | 00:00:06 |
|* 4 | TABLE ACCESS FULL  | SIGNATURE          | 40M  | 1042M |       | 146K (1)   | 00:00:06 |
|* 5 | TABLE ACCESS FULL  | STOCKAGE           | 48M  | 3322M |       | 130K (2)   | 00:00:06 |
| 6  | TABLE ACCESS FULL  | ALLOTISSEMENT_FLUX | 49M  | 5527M |       | 536K (1)   | 00:00:21 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("AF"."ID"="SIG"."ID_FLUX")
2 - access("ST"."ID_FLUX"="SIG"."ID_FLUX")
4 - filter("SIG"."NB_APPEL_SERVICE_SIGNATURE"<4 AND "SIG"."STATUT_SIGNATURE"='SIGNE' AND "SIG"."DATE_STATUT_SIGNATURE">=SYSDATE@!-1000)
5 - filter("ST"."STATUT_STOCKAGE"='OUI' AND "ST"."DATE_STATUT_STOCKAGE">=SYSDATE@!-1000)

另外还存在Table access rowid无法生效的问题,想请教各位怎么解决?


问题分析与解决建议

1. 索引提示语法错误,根本没生效

你写的/* index( ) */是无效的——Oracle的提示语法需要加+号,而且必须明确指定表别名和索引名称,否则优化器根本识别不到。正确的写法应该是这样(假设signature表的复合索引叫idx_sig_combined):

SELECT af.ID, af.nom_flux, st.chemin_stockage, af.hash_flux 
FROM stockage st 
INNER JOIN allotissement_flux af ON EXISTS (
    SELECT * FROM signature sig /*+ index(sig idx_sig_combined) */
    WHERE st.id_flux = sig.id_flux 
    AND af.ID = sig.id_flux 
    AND sig.statut_signature = 'SIGNE' 
    AND sig.nb_appel_service_signature < 4 
    AND sig.date_statut_signature >= sysdate - 1000
) 
WHERE st.statut_stockage = 'OUI' 
AND st.date_statut_stockage >= sysdate - 1000

2. 数据量太大,全表扫描反而更高效

从执行计划看,signature表返回了40M行,stockage返回48M行,allotissement_flux返回49M行——这些数据量几乎接近全表了。当过滤条件(比如sysdate-1000)返回的行数超过全表的10%-20%时,Oracle会认为全表扫描的成本比走索引更低(因为索引需要回表取数据,IO成本更高),所以就算你加了正确的提示,它也可能忽略。

这种情况下,你需要重新评估业务需求:是否真的需要查询1000天内的所有数据?如果可以缩小时间范围,过滤后的行数减少,索引自然会被优化器优先选择。

3. 单字段索引没用,要建复合覆盖索引

你给每个字段单独建了索引,但这种单字段索引在多条件过滤+关联的场景下几乎没用。比如signature表的过滤条件是statut_signature='SIGNE'、nb_appel_service_signature<4、date_statut_signature>=sysdate-1000,还要关联id_flux,你应该建一个复合覆盖索引:

CREATE INDEX idx_sig_combined ON signature (statut_signature, nb_appel_service_signature, date_statut_signature, id_flux);

这个索引包含了所有过滤条件和关联字段,优化器可以直接通过索引获取需要的数据,不需要回表(也就不会出现TABLE ACCESS ROWID的需求了)。

同理,stockage表可以建复合索引:

CREATE INDEX idx_stock_combined ON stockage (statut_stockage, date_statut_stockage, id_flux);

4. 统计信息过时,优化器判断错误

如果你的表数据有大量插入/更新,但很久没更新统计信息,Oracle会基于旧的统计数据判断执行计划,导致错误选择全表扫描。你可以手动更新统计信息:

-- 更新单表统计信息
DBMS_STATS.GATHER_TABLE_STATS('你的用户名', 'SIGNATURE', CASCADE => TRUE);
DBMS_STATS.GATHER_TABLE_STATS('你的用户名', 'STOCKAGE', CASCADE => TRUE);
DBMS_STATS.GATHER_TABLE_STATS('你的用户名', 'ALLOTISSEMENT_FLUX', CASCADE => TRUE);

5. 子查询写法可以优化,让优化器更好识别索引

把EXISTS子查询改成直接JOIN的形式,可能让优化器更容易评估索引的使用价值:

SELECT DISTINCT af.ID, af.nom_flux, st.chemin_stockage, af.hash_flux 
FROM stockage st 
JOIN signature sig ON st.id_flux = sig.id_flux
JOIN allotissement_flux af ON af.ID = sig.id_flux
WHERE sig.statut_signature = 'SIGNE' 
  AND sig.nb_appel_service_signature < 4 
  AND sig.date_statut_signature >= sysdate - 1000
  AND st.statut_stockage = 'OUI' 
  AND st.date_statut_stockage >= sysdate - 1000;

这里用DISTINCT代替原来EXISTS的去重逻辑,优化器可能会选择走上面建的复合索引。


内容的提问来源于stack exchange,提问作者Ipromise

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:32:56