如何在PL / SQL中内联变量?
-
27-10-2019 - |
题
情况
对于Oracle 11.2.0.2.0中对大量数据的中型查询的查询执行计划,我遇到了一些麻烦。为了加快速度,我介绍了一个范围过滤器,该过滤器的功能大致如下: 通用标签
如您所见,我想使用可选的组织编号范围来限制JOIN
的organisations
。客户端代码可以在有限制(应该很快)或没有限制(非常慢)的情况下调用DO_STUFF
。
麻烦
麻烦的是,PL / SQL将为上述org_from
和org_to
参数创建绑定变量,这是我在大多数情况下期望的:
通用标签
解决方法
仅在这种情况下,当我仅内联这些值时,即当Oracle执行的查询实际上类似于时,我认为查询执行计划要好得多。 通用标签
“很多”是指速度提高5-10倍。请注意,查询很少执行,即每月执行一次。因此,我不需要缓存执行计划。
我的问题
-
如何在PL / SQL中内联值?我知道立即执行,但我希望拥有PL / SQL编译我的查询,而不执行字符串连接。
-
我只是测量偶然发生的事情还是可以假设内联变量确实更好(在这种情况下)?我之所以这样问,是因为我认为绑定变量迫使Oracle制定一般执行计划,而内联值将允许分析非常具体的列和索引统计。所以我可以想象这不仅仅是一个巧合。
-
我错过了什么吗? 也许除了变量内联之外,还有完全其他方法可以实现查询执行计划的改进(请注意,我也尝试了很多提示,但我不是该领域的专家)?
如何在PL / SQL中内联值?我知道立即执行,但我希望拥有PL / SQL编译我的查询,而不执行字符串连接。
我只是测量偶然发生的事情还是可以假设内联变量确实更好(在这种情况下)?我之所以这样问,是因为我认为绑定变量迫使Oracle制定一般执行计划,而内联值将允许分析非常具体的列和索引统计。所以我可以想象这不仅仅是一个巧合。
我错过了什么吗? 也许除了变量内联之外,还有完全其他方法可以实现查询执行计划的改进(请注意,我也尝试了很多提示,但我不是该领域的专家)?
解决方案
在您的评论之一中,您说:
“此外,我还检查了各种绑定值。 使用绑定变量,我得到了一些全 TABLE SCANS,而使用硬编码 值,该计划看起来要好得多。”
有两条路径。如果为参数传递NULL,则选择所有记录。在这种情况下,全表扫描是检索数据的最有效方法。如果您传递值,则索引读取可能会更有效,因为您只选择了一小部分信息。
当您使用绑定变量制定查询条件时,优化器必须做出决定:应该假设大多数情况下您将传递值还是传递空值?难。因此,换一种方式来看:仅需要选择记录的子集时进行全表扫描,还是需要选择所有记录时进行索引读取是否效率较低?
似乎优化器已经充分考虑进行全表扫描,因为它是覆盖所有事件的效率最低的操作。
当您对值进行硬编码时,Optimizer会立即知道
10 IS NULL
评估为FALSE,因此它可以权衡使用索引读取来查找所需子集记录的优点。
那该怎么办?正如您所说的,该查询每月仅运行一次,我认为只需要对业务流程进行少量更改即可拥有单独的查询:一个查询适用于所有组织,另一个查询适用于一组组织。
“顺便说一句,删除了:R1 IS NULL子句 不会改变执行计划 很多,剩下的我 条件的另一侧,:R1 <= org.no NULL毫无意义 无论如何,因为org.no不为空”
好的,所以事情是,您有一对绑定变量,它们指定一个范围。根据值的分布,不同的范围可能适合不同的执行计划。也就是说,此范围(可能)适合索引范围扫描... 通用标签
...而这很可能更适合全表扫描... 通用标签
这就是绑定变量偷看起作用的地方。
(当然取决于值的分布)。
其他提示
由于查询计划实际上始终是不同的,这意味着优化器的基数估计由于某种原因而关闭。您可以从查询计划中确认使用绑定变量时优化器期望条件选择的不足吗?由于您使用的是11.2,因此Oracle应该使用自适应游标共享,因此它不应该是绑定变量偷看的问题(假设您在测试中多次调用带有绑定变量的版本,且该绑定变量具有不同的NO
值。
好的计划的基数估计实际上正确吗?我知道您说过,NO
列上的统计信息是准确的,但是例如,您可能无法通过常规的统计信息收集过程来更新流浪直方图。
您始终可以在查询中使用提示来强制使用特定的索引(尽管使用存储的轮廓或优化器计划的稳定性。这些选项中的任何一个都比诉诸动态SQL更为可取。
但是,要尝试的另一项测试是将SQL 99连接语法替换为Oracle的旧语法,即 通用标签
这显然不应更改任何内容,但SQL 99语法存在解析器问题,因此需要检查。
闻起来像Bind Peeking ,但是我只在Oracle 10上使用,所以我不能声称11中存在相同的问题。
这看起来很像是需要自适应游标共享以及SQLPlan稳定性。
我认为正在发生的是capture_sql_plan_baselines parameter is true
。与use_sql_plan_baselines
相同。如果是这样,则会发生以下情况:
- 第一次分析查询时,它会得到一个新的计划。
- 第二次将此计划作为可接受的计划存储在sql_plan_baselines中。
- 此查询的所有后续运行均使用此计划,而不管绑定变量是什么。
如果“自适应游标共享”已处于活动状态,则优化器将生成一个新的/更好的计划,将其存储在sql_plan_baselines中,但无法使用它,直到有人接受此较新的计划作为可接受的替代计划为止。检查
dba_sql_plan_baselines
,看看您的查询中是否包含accepted = 'NO' and verified = null
条目 如果该计划的性能比没有新计划的性能至少好1.5倍,则可以使用dbms_spm.evolve
来开发新计划并使其自动被接受。我希望这会有所帮助。
我将此添加为评论,但也会在此处提供。希望这不会过于简单化,看着详细的回答,我可能会误解确切的问题,但是无论如何...
似乎您的组织结构表具有定义为数字的列号(org.no)。在您的硬编码示例中,您使用 numbers 进行比较。 通用标签
在您的过程中,您要传入 varchar2 : 通用标签
因此,要将varchar2与数字进行比较, Oracle将必须进行转换,因此这可能会导致完整扫描。
解决方案:更改proc以传递数字