Oracle EXECUTE IMMEDIATE更改解释查询计划

时间:2010-05-07 16:12:47

标签: oracle performance sql-execution-plan execute-immediate

我有一个存储过程,我使用EXECUTE IMMEDIATE调用。我面临的问题是,当我使用EXECUTE IMMEDIATE调用该过程时直接调用该过程时,解释计划是不同的。这导致执行时间增加5倍。计划之间的主要区别在于,当我使用execute immediate时,优化器不会取消子查询(我使用NOT EXISTS条件)。我们在这里使用基于规则的优化器来处理大多数查询,但是这个提示使用索引以便使用CBO(但是,我们不收集表上的统计信息)。我们正在运行Oracle9i企业版9.2.0.4.0版 - 64位生产。

实施例: 快:

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)

良好的解释计划:

OPERATION       OPTIONS         OBJECT_NAME     OBJECT_TYPE     ID      PARENT_ID
SELECT STATEMENT                                                0       
SORT            GROUP BY                                        1       0
NESTED LOOPS                                                    2       1
HASH JOIN       ANTI                                            3       2
TABLE ACCESS    BY INDEX ROWID  H                               4       3
NESTED LOOPS                                                    5       4
NESTED LOOPS                                                    6       5
NESTED LOOPS                                                    7       6
TABLE ACCESS    FULL            B                               8       7
TABLE ACCESS    BY INDEX ROWID  A                               9       7
INDEX           RANGE SCAN      IDX_A_1         UNIQUE          10      9
INDEX           UNIQUE SCAN     G               UNIQUE          11      6
INDEX           RANGE SCAN      H_UK            UNIQUE          12      5
TABLE ACCESS    FULL            F                               13      3
TABLE ACCESS    FULL            DOW                             14      2

糟糕的解释计划:

OPERATION       OPTIONS         OBJECT_NAME     OBJECT_TYPE     ID      PARENT_ID
SELECT STATEMENT                                                0       
SORT            GROUP BY                                        1       0
NESTED LOOPS                                                    2       1
NESTED LOOPS                                                    3       2
NESTED LOOPS                                                    4       3
NESTED LOOPS                                                    5       4
TABLE ACCESS    FULL            B                               6       5
TABLE ACCESS    BY INDEX ROWID  A                               7       5
INDEX           RANGE SCAN      IDX_A_1         UNIQUE          8       7
TABLE ACCESS    FULL            F                               9       8
INDEX           UNIQUE SCAN     G               UNIQUE          10      4
TABLE ACCESS    BY INDEX ROWID  H                               11      3
INDEX           RANGE SCAN      H_UK            UNIQUE          12      11
TABLE ACCESS    FULL            DOW                             13      2

在糟糕的解释计划中,子查询没有被取消。通过向子查询添加no_unnest提示,我能够重现错误的计划;但是,我无法使用不必要的提示重现好计划(使用execute immediate运行程序时)。当使用execute immediate而不是不必要的提示时,优化器正在考虑其他提示。

仅当我使用execute immediate来调用该过程时才会出现此问题。如果我在查询本身使用execute immediate,它就会使用好的计划。

4 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

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

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

更极端的步骤是10053追踪。在Jonathan Lewis的网站上发布了viewer。这可以让你深入了解优化的内容,试图找出不同计划的原因。

从更广泛的角度来看,9i已经死了,RBO已经死了。我正在认真评估一个项目,将应用程序转移到CBO。有些功能会强制使用CBO,如果没有统计数据,这种问题就会不断出现。

答案 2 :(得分:1)

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

执行立即提供错误的查询计划[ID 398605.1]

Modified 09-NOV-2006     Type PROBLEM     Status MODERATED

本文档是通过Oracle支持部门的快速可见性(RaV)流程提供给您的,因此未经过独立的技术审核。

适用于: Oracle Server - Enterprise Edition - 版本:9.2.0.6 任何平台上都可能出现此问题。

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

<强>原因 已发现并在未发布的错误2906307中验证了此问题的原因。 它是由PLSQL以递归方式发出的SQL语句引起的 深度大于1可能会获得与直接从SQL发出的执行计划不同的执行计划。 有多个优化器功能受此错误影响(例如_unnest_subquery,_pred_move_around = true) 与特征相关的提示也可以被忽略。

此错误包含与错误2871645相同的基本问题。复杂视图合并不会发生 递归SQL&gt;深度1但适用于复杂视图合并以外的功能。

错误2906307作为Bug 3182582的重复关闭SQL STATEMENT RUN SLOWER DBMS_JOB比SQL * PLUS更重要。 它固定在10.2

<强>解决方案 对于insert语句,请使用提示BYPASS_RECURSIVE_CHECK: INSERT / * + BYPASS_RECURSIVE_CHECK * / INTO表

<强>参考 BUG:2871645 - 复杂的视图合并不会导致RECURSIVE SQL&gt;深度1 BUG:3182582 - DBMS_JOB中的SQL语句运行缓慢,而不是SQL * PLUS

答案 3 :(得分:1)

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

执行立即提供错误的查询计划[ID 398605.1]

修改后的09-NOV-2006类型问题状态已修改

本文档是通过Oracle支持部门的快速可见性(RaV)流程提供给您的,因此未经过独立的技术审核。

适用于:Oracle Server - Enterprise Edition - 版本:9.2.0.6任何平台上都可能出现此问题。

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

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

此错误涵盖与错误2871645相同的基本问题。递归SQL&gt;不会发生复杂视图合并深度1,但适用于复杂视图合并之外的其他功能。

错误2906307作为Bug 3182582的重复关闭SQL STATEMENT RUN SLOWER DBMS_JOB比SQL * PLUS更重要。它固定在10.2

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

参考文献BUG:2871645 - 复杂视图合并不会导致返回SQL&gt; DEPTH 1 BUG:3182582 - DBMS_JOB中的SQL语句运行缓慢,而不是SQL * PLUS