Oracle查询计划效率问题
以下查询在PL/SQL 过程
中给出。
SELECT e.data FROM extra e WHERE e.external_id in
(SELECT * FROM TABLE (p_external_ids)).
p_external_ids
的类型是创建或替换类型“VARCHAR2TABLE”作为 VARCHAR2(4000 CHAR) 表
。
Oracle 使用全表扫描执行查询的效率很低。 查询提示没有帮助,必要的索引已就位。使用硬编码 ID 替换 SELECT *
部分可将查询运行时间减少 20 倍
,当表中的行数为200 000时。
作为参考,使用 SELECT * FROM TABLE
子句执行大约需要 0.3 秒,对于单个硬编码 ID 大约需要 0.015 毫秒
。
建议使用哪些有效方法(键搜索)来编写存储过程以从表中提取多个 id 的数据?提供的集合类型必须用于将 id 列表传递给存储过程。
The following query is given in a PL/SQL procedure
.
SELECT e.data FROM extra e WHERE e.external_id in
(SELECT * FROM TABLE (p_external_ids)).
The type of p_external_ids
is create or replace type "VARCHAR2TABLE" as table of VARCHAR2(4000 CHAR)
.
Oracle inefficiently executes the query using a full table scan. Hints on query did not help and necessary indexes are in place. Replacing the SELECT *
part with hardcoded ids reduce query running time by a factor of 20
, when the number of rows in the table is 200 000.
For reference it takes about 0.3 sec to execute with SELECT * FROM TABLE
clause, and around 0.015 ms
for a single hardcoded id.
What are the suggested efficient ways (key search) to write a stored procedure to extract the data from the table for multiple ids? The provided collection type must be used to pass in the list of ids to a stored procedure.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您尝试了哪些提示?
你能发布快速和慢速查询计划吗?
在 SQL 中使用 PL/SQL 集合的常见问题之一是 CBO 经常错误地猜测集合中的元素数量,并因此选择错误的计划。在这些情况下,使用 CARDINALITY 提示通常很有帮助,即
告诉优化器期望 P_EXTERNAL_IDS 中有 10 个元素。
Tom Kyte 对 askTom 上的基数提示和 PL/SQL 集合。
EXTERNAL_ID 列的数据类型是什么?您的集合是字符串的集合,但 EXTERNAL_ID 往往意味着数字。这里真的存在数据类型不匹配吗?
仅当问题是优化器在引用集合时无法获得准确的基数估计,但在引用临时表时可以获得准确的基数估计时,将集合复制到临时表才会有帮助。如果您正确指定了 CARDINALITY 提示并且不会改变性能,则意味着问题不在于优化器的基数估计。
您能发布快速和慢速查询计划吗?
您能否发布您正在使用的包含 CARDINALITY 提示的确切 SQL 语句(可能存在语法错误)
What hints did you try?
Can you post the fast and the slow query plan?
One of the general issues with using PL/SQL collections in SQL is that the CBO often guesses incorrectly at the number of elements in the collection and chooses the wrong plan as a result. It is often helpful in those cases to use the CARDINALITY hint, i.e.
tells the optimizer to expect 10 elements in P_EXTERNAL_IDS.
Tom Kyte has a more in depth discussion about the cardinality hint and PL/SQL collections on askTom as well.
What is the data type of the EXTERNAL_ID column? Your collection is a collection of strings but EXTERNAL_ID tends to imply a NUMBER. Is there really a data type mismatch here?
Copying the collection into a temporary table would only be expected to help if the problem was that the optimizer couldn't get an accurate cardinality estimate when you referenced the collection but it could get an accurate estimate when you referenced the temporary table. If you are correctly specifying the CARDINALITY hint and that doesn't change performance, that would imply that the problem is not with the optimizer's cardinality estimates.
Can you post the fast and the slow query plans?
Can you post the exact SQL statement you are using that includes the CARDINALITY hint (perhaps there is a syntax error)
我相信它正在进行全面扫描,因为它无法预测 p_external_ids 是否会大于或小于盈亏平衡点。
我的意思是:
如果执行单个索引查找的成本为 200,执行全表扫描的成本为 100000,如果您查找 20 个值,则总成本将为 4000(小于 100000)。但如果您要查找 1000 个值,则使用索引的总成本将为 200000。
I believe it is doing a full scan because it can't predict if the p_external_ids is going to be larger or smaller than the break even point.
What I mean:
If it costs 200 to do a single index lookup, and 100000 to do a full table scan, if you are looking up 20 values, total cost will be 4000 (less than 100000). But if you are looking up 1000 values, the total cost using the indices would be 200000.
这个问题在设置管道和表的基数上得到了非常令人满意的回答函数,所以请阅读全文!
摘要:
方法:可扩展优化器
以下引用和示例来自文章,但经过一定程度的调整以一致地回答问题。
1)制作一个包装函数
2)创建接口类型
和主体
3) 将函数与接口类型相关联,如下所示。
4)现在像这样使用这个函数:
This question is answered in a very satisfying way on setting cardinality for pipelined and table functions, so please go and read the full article!
Summary:
method: extensible optimiser
The following quotes and examples come from the article but are somewhat adapted to answer the question consistently.
1) Make a wrapper function
2) Make an interface type
and the body
3) Associate the function with the interface type, as follows.
4) Use this function now like this: