对于Oracle 11.2.0.2.0中大量数据的中型查询,我的查询执行计划有些麻烦.为了加快速度,我引入了一个大致如下的范围过滤器:
PROCEDURE DO_STUFF( org_from VARCHAR2 := NULL,org_to VARCHAR2 := NULL) -- [...] JOIN organisations org ON (cust.org_id = org.id AND ((org_from IS NULL) OR (org_from <= org.no)) AND ((org_to IS NULL) OR (org_to >= org.no))) -- [...]
如您所见,我想限制使用可选范围的组织编号的组织的联合.客户端代码可以调用(假定为快)或不(非常慢)限制的DO_STUFF.
麻烦
麻烦的是,PL / sql将为上述org_from和org_to参数创建绑定变量,这在大多数情况下将是我期望的:
-- [...] JOIN organisations org ON (cust.org_id = org.id AND ((:B1 IS NULL) OR (:B1 <= org.no)) AND ((:B2 IS NULL) OR (:B2 >= org.no))) -- [...]
只有在这种情况下,当我只是内联值时,我测量出查询执行计划好多了,即当Oracle执行的查询实际上是这样的
-- [...] JOIN organisations org ON (cust.org_id = org.id AND ((10 IS NULL) OR (10 <= org.no)) AND ((20 IS NULL) OR (20 >= org.no))) -- [...]
“很多”,我的意思是快5-10倍.请注意,查询执行非常少,即每月一次.所以我不需要缓存执行计划.
我的问题
>如何在PL / sql中进行内联值?我知道大约EXECUTE IMMEDIATE,但我更喜欢PL / sql编译我的查询,而不是做字符串连接.
>我只是测量偶然发生的事情,还是假设内联变量确实更好(在这种情况下)?我问的原因是因为我认为绑定变量强制Oracle设计一个一般的执行计划,而内联值将允许分析非常具体的列和索引统计信息.所以我可以想象这不仅仅是巧合.
我错过了什么吗?也许完全有其他的方式来实现查询执行计划的改进,除了变量内联(注意我已经尝试了很多提示,但我不是那个领域的专家)?
“Also I checked varIoUs bind values.
With bind variables I get some FULL
TABLE SCANS,whereas with hard-coded
values,the plan looks a lot better.”
有两条路.如果您为参数传递NULL,那么您正在选择所有记录.在这种情况下,全表扫描是检索数据的最有效方法.如果您传递值,则索引读取可能会更有效率,因为您只选择一小部分信息.
当您使用绑定变量制定查询时,优化器必须做出决定:是否应该假设大部分时间将传递值,否则您将通过null?难.所以看另外一种方法:当你只需要选择一个记录子集,或者当你需要选择所有的记录时,做索引读取,做全表扫描效率会更低吗?
看起来优化器已经全面扫描了全面扫描,因为它是覆盖所有可能性的最低效的操作.
而当您硬编码值时,Optimizer立即知道10 IS NULL评估为FALSE,因此它可以权衡使用索引读取的优点,以找到所需的子集记录.
那么该怎么办?正如你所说的,这个查询只能运行一个月一次,我认为只需要对业务流程进行一些小的更改,以便分开查询:一个用于所有组织,一个用于一个子组织.
“Btw,removing the :R1 IS NULL clause
doesn’t change the execution plan
much,which leaves me with the other
side of the OR condition,:R1 <=
org.no where NULL wouldn’t make sense
anyway,as org.no is NOT NULL”
好的,所以你有一对绑定变量指定一个范围.根据值的分布,不同的范围可能适合不同的执行计划.也就是说,这个范围(可能)适合索引范围扫描…
WHERE org.id BETWEEN 10 AND 11
…而这可能更适合于全表扫描…
WHERE org.id BETWEEN 10 AND 1199999
那就是“变形偷窥”发挥作用的地方.
(当然取决于值的分布).