Oracle查询计划效率问题

时间:2010-11-15 14:09:20

标签: sql oracle plsql

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使用全表扫描无效地执行查询。 查询提示没有帮助,必要的索引已经到位。使用硬编码的ID替换SELECT *部分可以减少查询运行时间factor of 20,当时行数为表格 200 000

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

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

3 个答案:

答案 0 :(得分:7)

你尝试了什么提示? 你能发布快速和慢速的查询计划吗?

在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语句(可能存在语法错误)

答案 1 :(得分:1)

我认为它正在进行全面扫描,因为它无法预测p_external_ids是否会大于或小于收支平衡点。

我的意思是:

如果执行单个索引查找需要200,而执行全表扫描需要100000,如果查找20个值,则总成本将为4000(小于100000)。但如果您查找1000个值,使用索引的总成本将为200000.

答案 2 :(得分:0)

setting cardinality for pipelined and table functions上以非常令人满意的方式回答了这个问题,所以请继续阅读完整的文章!

要点:

方法:可扩展优化器

  

可扩展优化器由Oracle数据盒式磁带实现(它本质上是一种对象类型,称为接口类型,包含一个或多个定义明确的结构化方法)。此功能使我们能够设计自己的基数计算(作为接口类型中的指定方法),然后将它们与我们的表或流水线函数相关联。在查询优化期间,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'));