【问题标题】:Oracle EXECUTE IMMEDIATE changes explain plan of queryOracle EXECUTE IMMEDIATE 更改解释查询计划
【发布时间】:2011-02-16 21:56:22
【问题描述】:

我有一个使用 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)

好的解释计划:

OPERATION OPTIONS 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

糟糕的解释计划:

OPERATION OPTIONS 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,它会使用好的计划。

【问题讨论】:

  • 我认为基于规则的 oprimizer 应该是一致的。也许添加一个提示,看看这是否也会改变计划 - 以验证 RBO 是否实际生效......
  • 我们已经提示在查询中使用索引,那么这是否会导致使用 CBO 代替查询?即便如此,我尝试向子查询添加不嵌套的提示,但它似乎不尊重提示。
  • 索引提示不会导致使用 CBO。您可以使用 FIRST_ROWS 或 ALL_ROWS 提示强制使用 CBO,或使用 RULE 提示强制使用 RBO。也许您应该发布实际的 SQL 查询和您看到的两个不同的计划。
  • 请发布实际查询和生成的计划,然后也许我们可以提供建议。一件事:如果你的桌子上没有统计数据,我的经验是,CBO 几乎肯定会给你一个糟糕的计划。您运行的是哪个版本的 Oracle?
  • 我在帖子中包含了查询和两个解释计划。

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


【解决方案1】:

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

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

【讨论】:

  • 我已经尝试在这个查询中同时使用 ANSI 和非 ANSI 连接。原始查询不是 ANSI,但我最终粘贴了 ANSI 查询。两种样式我仍然有同样的问题。
【解决方案2】:

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

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

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

更极端的一步是 10053 跟踪。 Jonathan Lewis 的网站上发布了viewer。这可以让您深入了解优化,尝试找出不同计划的原因。

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

【讨论】:

    【解决方案3】:

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

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

    Modified 09-NOV-2006     Type PROBLEM     Status MODERATED
    

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

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

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

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

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

    错误 2906307 已作为错误 3182582 SQL 语句在 DBMS_JOB 中运行比在 SQL*PLUS 中慢的副本而关闭。 它已在 10.2 中修复

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

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

    【讨论】:

    • 10.2.0.4 中仍在发生类似的事情
    【解决方案4】:

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

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

    修改 09-NOV-2006 类型问题状态 MODERATED

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

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

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

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

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

    错误 2906307 已作为错误 3182582 SQL 语句在 DBMS_JOB 中运行比在 SQL*PLUS 中慢的副本而关闭。它已在 10.2 中修复

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

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

    【讨论】:

      猜你喜欢
      • 2011-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      • 2012-11-12
      • 2012-12-16
      相关资源
      最近更新 更多