【问题标题】:Oracle query plan efficiency problemoracle查询计划效率问题
【发布时间】:2016-08-02 17:39:35
【问题描述】:

PL/SQL procedure 中给出了以下查询。

SELECT e.data FROM extra e WHERE e.external_id in
    (SELECT * FROM TABLE (p_external_ids)).

p_external_ids 的类型是create or replace type "VARCHAR2TABLE" as table of VARCHAR2(4000 CHAR)

Oracle 使用全表扫描执行查询效率低下。 查询提示没有帮助,必要的索引已经到位。 当表中的行数为 200 000

作为参考,使用 SELECT * FROM TABLE 子句执行大约需要 0.3 秒,对于单个硬编码 id 大约需要 0.015 ms

编写存储过程以从多个ID的表中提取数据的建议有效方法(关键字搜索)有哪些?提供的集合类型必须用于将 id 列表传递给存储过程。

【问题讨论】:

    标签: sql oracle plsql


    【解决方案1】:

    您尝试了哪些提示? 你能发布快速和慢速查询计划吗?

    在 SQL 中使用 PL/SQL 集合的一个普遍问题是 CBO 经常错误地猜测集合中的元素数量并因此选择了错误的计划。在这些情况下,使用 CARDINALITY 提示通常很有帮助,即

    SELECT e.data 
      FROM extra e
     WHERE e.external_id IN (
        SELECT /*+ cardinality(ids 10) */ *
          FROM TABLE( p_external_ids ) ids
      )
    

    告诉优化器期望 P_EXTERNAL_IDS 中有 10 个元素。

    Tom Kyte 在 askTom 上也对 the cardinality hint and PL/SQL collections 进行了更深入的讨论。

    EXTERNAL_ID 列的数据类型是什么?您的集合是字符串的集合,但 EXTERNAL_ID 往往暗示一个 NUMBER。这里真的是数据类型不匹配吗?

    仅当问题是当您引用该集合时优化器无法获得准确的基数估计但当您引用临时表时它可以得到准确的估计时,将集合复制到临时表中才会有所帮助。如果您正确指定了 CARDINALITY 提示并且这不会改变性能,这意味着问题不在于优化器的基数估计。

    你能发布快速和慢速查询计划吗? 您能否发布您正在使用的包含 CARDINALITY 提示的确切 SQL 语句(可能存在语法错误)

    【讨论】:

    • 基数提示似乎没有帮助。可以使用临时表来解决这个问题吗? IE。在临时表上进行连接,而不是收集?如果使用具有命名列的集合(即不是select *)会有什么不同
    • 谢谢贾斯汀!您的响应有助于使用 cardinality 提示和具体化集合来解决问题:)
    【解决方案2】:

    我相信它正在进行全面扫描,因为它无法预测 p_external_ids 是大于还是小于盈亏平衡点。

    我的意思:

    如果进行一次索引查找花费 200,进行一次全表扫描花费 100000,如果您要查找 20 个值,则总成本将是 4000(小于 100000)。但是,如果您要查找 1000 个值,则使用索引的总成本将是 200000。

    【讨论】:

    • 对,集合中的元素数量预计不会很大。通常元素很少,但有时可能是 100。目前我只尝试过很少的元素,它会进行全表扫描。 cardinality 提示没有帮助...
    【解决方案3】:

    这个问题在setting cardinality for pipelined and table functions上得到了非常满意的回答,所以请去阅读全文!


    总结:

    方法:可扩展优化器

    可扩展优化器由 Oracle Data Cartridge 实现(本质上是一种对象类型,称为接口类型,包含一个或多个定义明确的结构化方法)。此功能使我们能够设计自己的基数计算(作为接口类型中的规定方法),然后将它们与我们的表或流水线函数相关联。类型的基数方法由 CBO 在查询优化期间调用,以确定流水线或表函数的行数。

    以下引用和示例来自文章,但在某种程度上适用于一致地回答问题。

    1) 制作包装函数

    我们将创建一个小函数来接收和返回我们的通用 VARCHAR2TABLE 类型的集合。这个函数对集合本身没有任何作用;它只是一个包装器。

    SQL> CREATE FUNCTION card_varchar2(
      2                  p_collection IN varchar2table
      3                  ) RETURN varchar2table IS
      4  BEGIN
      5     RETURN p_collection;
      6  END card_varchar2;
      7  /
    
    Function created.
    

    2) 制作接口类型

    其次,我们将创建一个与我们简单的 card_varchar2 函数相关联的接口类型规范,如下所示。

    SQL> CREATE TYPE card_varchar2_ot AS OBJECT (
      2
      3     dummy_attribute NUMBER,
      4
      5     STATIC FUNCTION ODCIGetInterfaces (
      6                     p_interfaces OUT SYS.ODCIObjectList
      7                     ) RETURN NUMBER,
      8
      9     STATIC FUNCTION ODCIStatsTableFunction (
     10                     p_function   IN  SYS.ODCIFuncInfo,
     11                     p_stats      OUT SYS.ODCITabFuncStats,
     12                     p_args       IN  SYS.ODCIArgDescList,
     13                     p_collection IN varchar2table
     14                     ) RETURN NUMBER
     15
     16  );
     17  /
    
    Type created.
    

    和身体

    SQL> CREATE TYPE BODY card_varchar2_ot AS
      2
      3     STATIC FUNCTION ODCIGetInterfaces (
      4                     p_interfaces OUT SYS.ODCIObjectList
      5                     ) RETURN NUMBER IS
      6     BEGIN
      7        p_interfaces := SYS.ODCIObjectList(
      8                           SYS.ODCIObject ('SYS', 'ODCISTATS2')
      9                           );
     10        RETURN ODCIConst.success;
     11     END ODCIGetInterfaces;
     12
     13     STATIC FUNCTION ODCIStatsTableFunction (
     14                     p_function   IN  SYS.ODCIFuncInfo,
     15                     p_stats      OUT SYS.ODCITabFuncStats,
     16                     p_args       IN  SYS.ODCIArgDescList,
     17                     p_collection IN  varchar2table
     18                     ) RETURN NUMBER IS
     19     BEGIN
     20        p_stats := SYS.ODCITabFuncStats(p_collection.COUNT);
     21        RETURN ODCIConst.success;
     22     END ODCIStatsTableFunction;
     23
     24  END;
     25  /
    
    Type body created.
    

    3) 将函数与接口类型关联起来,如下。

    SQL> ASSOCIATE STATISTICS WITH FUNCTIONS card_varchar2 USING card_varchar2_ot;
    
    Statistics associated.
    

    4) 现在像这样使用这个函数:

    SQL> SELECT *
      2  FROM   TABLE(card_varchar2('A','B','C'));
    

    【讨论】:

      猜你喜欢
      • 2012-09-02
      • 2011-01-19
      • 2011-01-25
      • 1970-01-01
      • 2015-10-20
      • 1970-01-01
      • 2011-01-04
      • 2011-05-21
      • 2019-01-29
      相关资源
      最近更新 更多