3

我有一个使用 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 调用该过程时才会出现此问题。如果我在查询本身上使用立即执行,它会使用好的计划。

4

4 回答 4

1

您使用了 ANSI 连接语法,这将强制使用 CBO(请参阅http://jonathanlewis.wordpress.com/2008/03/20/ansi-sql/

“一旦你在没有统计数据的情况下运行基于成本的运行,就会有各种各样的小事情可能足以导致执行计划中出现意外行为。”

于 2010-05-08T05:58:56.173 回答
1

您可以采取几个步骤。第一个是 10046 跟踪。

理想情况下,我会在执行“好”和“坏”查询的单个会话上开始跟踪。跟踪文件应该包含两个带有硬解析的查询。我会对为什么第二个有硬解析感兴趣,因为如果它具有相同的 SQL 结构和相同的解析用户,那么第二个硬解析没有太多理由。相同的会话应该意味着不同的内存设置等没有奇怪之处。

SQL 没有显示任何变量的使用,因此应该没有数据类型问题。所有列都“绑定”到表别名,因此似乎没有将变量与列混淆的范围。

更极端的一步是 10053 迹线。在 Jonathan Lewis 的网站上发布了一个查看器。这可以让您深入了解优化的内容,以尝试找出不同计划的原因。

从更广泛的角度来看,9i 几乎已经死了,而 RBO 几乎已经死了。我会认真评估将应用程序移至 CBO 的项目。有些功能会强制使用 CBO,如果没有统计信息,这种问题会不断出现。

于 2010-05-09T23:11:12.900 回答
1

事实证明,这是 Oracle 9i 中的一个已知错误。以下是错误报告中的文本。

立即执行会产生错误的查询计划 [ID 398605.1]

Modified 09-NOV-2006     Type PROBLEM     Status MODERATED

本文档通过 Oracle 支持的快速可见性 (RaV) 流程交付给您,因此未经独立技术审查。

适用于: Oracle 服务器 - 企业版 - 版本:9.2.0.6 此问题可能出现在任何平台上。

症状 当程序通过立即执行运行时,生成的计划与直接运行程序时不同。

原因 这个问题的原因已经在一个未发布的Bug 2906307 中被确定和验证。这是由于从PLSQL 发出的递归深度大于1 的SQL 语句可能与直接从SQL 发出的执行计划不同。有多个优化器功能受此错误影响(例如 _unnest_subquery,_pred_move_around=true)与功能相关的 HINTS 也可能被忽略。

此错误涵盖的基本问题与错误 2871645 复杂视图合并不会发生在递归 SQL > 深度 1 但对于复杂视图合并以外的功能相同。

错误 2906307 已作为错误 3182582 SQL STATEMENT RUN SLOWER IN DBMS_JOB THAN SLOWER IN SQL*PLUS 的副本关闭。它在 10.2 中已修复

解决方案 对于插入语句使用提示 BYPASS_RECURSIVE_CHECK: INSERT /*+ BYPASS_RECURSIVE_CHECK */ INTO table

参考 BUG:2871645 - 复杂视图合并不会发生在递归 SQL > 深度 1 BUG:3182582 - SQL 语句在 DBMS_JOB 中的运行速度比在 SQL*PLUS 中慢

于 2010-05-13T21:01:31.817 回答
1

事实证明,这是 Oracle 9i 中的一个已知错误。以下是错误报告中的文本。

立即执行会产生错误的查询计划 [ID 398605.1]

修改 2006 年 11 月 9 日类型问题状态中度

本文档通过 Oracle 支持的快速可见性 (RaV) 流程交付给您,因此未经独立技术审查。

适用于:Oracle 服务器 - 企业版 - 版本:9.2.0.6 此问题可能出现在任何平台上。

症状 当程序通过立即执行运行时,生成的计划与直接运行程序时不同。

原因 这个问题的原因已经在一个未发布的Bug 2906307 中被确定和验证。这是由于从PLSQL 发出的递归深度大于1 的SQL 语句可能与直接从SQL 发出的执行计划不同。有多个优化器功能受此错误影响(例如 _unnest_subquery,_pred_move_around=true)与功能相关的 HINTS 也可能被忽略。

此错误涵盖的基本问题与错误 2871645 复杂视图合并不会发生在递归 SQL > 深度 1 但对于复杂视图合并以外的功能相同。

错误 2906307 已作为错误 3182582 SQL STATEMENT RUN SLOWER IN DBMS_JOB THAN SLOWER IN SQL*PLUS 的副本关闭。它在 10.2 中已修复

解决方案 对于插入语句使用提示 BYPASS_RECURSIVE_CHECK: INSERT /*+ BYPASS_RECURSIVE_CHECK */ INTO table

参考 BUG:2871645 - 复杂视图合并不会发生在递归 SQL > 深度 1 BUG:3182582 - SQL 语句在 DBMS_JOB 中的运行速度比在 SQL*PLUS 中慢

于 2010-12-09T10:21:25.417 回答