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列表传递给存储过程。
答案 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'));