我有一个使用 EXECUTE IMMEDIATE 调用的存储过程。我面临的问题是,当我直接调用该过程与使用 EXECUTE IMMEDIATE 调用该过程时,解释计划是不同的。这导致执行时间增加了 5 倍。计划之间的主要区别在于,当我使用立即执行时,优化器不会取消嵌套子查询(我使用的是 NOT EXISTS 条件)。对于大多数查询,我们在这里使用基于规则的优化器,但是这个提示使用索引,因此正在使用 CBO(但是,我们不收集表上的统计信息)。我们正在运行 Oracle9i Enterprise Edition Release 9.2.0.4.0 - 64bit Production。
示例:快速:
begin
package.procedure;
end;
/
慢的:
begin
execute immediate 'begin package.' || proc_name || '; end;';
end;
/
询问:
SELECT /*+ INDEX(A IDX_A_1) */
a.store_cd,
b.itm_cd itm_cd,
CEIL ( (new_date - a.dt) / 7) week_num,
SUM (a.qty * b.demand_weighting * b.CONVERT) qty
FROM a
INNER JOIN
b
ON (a.itm_cd = b.old_itm_cd)
INNER JOIN
(SELECT g.store_grp_cd, g.store_cd
FROM g, h
WHERE g.store_grp_cd = h.fdo_cd AND h.fdo_type = '1') d
ON (a.store_cd = d.store_cd AND b.store_grp_cd = d.store_grp_cd)
CROSS JOIN
dow
WHERE a.dt BETWEEN dow.new_date - 91 AND dow.new_date - 1
AND a.sls_wr_cd = 'W'
AND b.demand_type = 'S'
AND b.old_itm_cd IS NOT NULL
AND NOT EXISTS
(SELECT
NULL
FROM f
WHERE f.store_grp_cd = a.store_cd
AND b.old_itm_cd = f.old_itm_cd)
GROUP BY a.store_cd, b.itm_cd, CEIL ( (dow.new_date - a.dt) / 7)
好的解释计划:
操作选项 OBJECT_NAME OBJECT_TYPE ID PARENT_ID 选择语句 0 按 1 0 分组 嵌套循环 2 1 哈希加入反 3 2 按索引 ROWID 访问表 H 4 3 嵌套循环 5 4 嵌套循环 6 5 嵌套循环 7 6 表访问完全 B 8 7 按索引 ROWID 访问表 A 9 7 索引范围扫描 IDX_A_1 唯一 10 9 索引唯一扫描 G 唯一 11 6 索引范围扫描 H_UK 唯一 12 5 表访问完全 F 13 3 表访问完全 DOW 14 2
糟糕的解释计划:
操作选项 OBJECT_NAME OBJECT_TYPE ID PARENT_ID 选择语句 0 按 1 0 分组 嵌套循环 2 1 嵌套循环 3 2 嵌套循环 4 3 嵌套循环 5 4 表访问完全 B 6 5 按索引 ROWID 访问表 A 7 5 索引范围扫描 IDX_A_1 唯一 8 7 表访问完全 F 9 8 索引唯一扫描 G 唯一 10 4 按索引 ROWID 访问表 H 11 3 索引范围扫描 H_UK 唯一 12 11 表访问完全 DOW 13 2
在糟糕的解释计划中,子查询没有被取消嵌套。通过向子查询添加 no_unnest 提示,我能够重现错误的计划;但是,我无法使用 unnest 提示重现好计划(当使用执行立即运行该过程时)。当使用立即执行而不是未嵌套提示时,优化器正在考虑其他提示。
仅当我使用 execute immediate 调用该过程时才会出现此问题。如果我在查询本身上使用立即执行,它会使用好的计划。