Kri*_*h T 2 oracle indexing performance query-optimization oracle12c
我需要查询一个表,并且可以根据前端选择的选项在 WHERE 子句中传递一个或最多六个参数。我在 WHERE 子句中使用了 CASE 语句来处理所有排列。
这是 SP 代码片段:
create procedure return_data (
p_field1 in varchar(20),
p_field2 in varchar2(30),
p_field3 in varchar2(30),
cur out sys_refcursor)
is
begin
open cur for
select col1, col2, col3,col4,col5,col6
from master_table
where (case when (p_field1 is null) then (1)
when (p_field1 is not null) and (col1=p_field1) then 1
else 0 end) =1
and (case when (p_field2 is null) then (1)
when (p_field2 is not null) and (col2=p_field2) then 1
else 0 end) =1
... so one repeat for all columns.
Run Code Online (Sandbox Code Playgroud)
但是,master_table 有超过 500 万行,因此我在每个搜索字段上创建了一个索引,但存储过程没有使用索引,而是进行全表扫描,导致性能下降。
如果我从 WHERE 子句中删除 CASE 语句并传递类似where col1=p_field1)索引的内容,则查询性能非常好。
但是,由于并非所有输入字段都是前端强制输入的,因此我必须在 WHERE 子句中使用 CASE 语句。
准确地说,对于 WHERE 子句中的 CASE 语句,Oracle 不使用索引。
有人可以建议如何调整上面的sql,以便它使用索引来获得快速性能吗?
提前致谢。
您编写的一个查询实际上是 64 (2^6) 个查询。不同的参数排列将适合不同的访问路径。
优化器根据其对表的了解为查询创建特定的访问路径。例如,WHERE 子句中使用的这些列的选择性如何?
这就是您设置优化器时面临的挑战:提出一个执行计划,该计划适用于用户传递的任何参数排列。当然不能那样做。问题是,有一种参数排列只能由全表扫描提供服务,而此时所有六个参数都为空。(您说存储过程每次至少传递一个参数,但这是从外部强制执行到查询的,因此优化器不知道这一点。)此外,某些索引列可能缺乏选择性,以致造成灾难性的后果。因此,优化器似乎选择使用全表扫描作为所有可能排列中危险性最小的方式,尽管这最终对于大多数排列来说效率较低。
对此该怎么办?
一种方法是要求优化器在每次运行时重新解析查询。使用行级安全性(错误地)执行此操作有多种复杂的方法 ,但也许您所需要做的就是将/*+ BIND_AWARE */提示应用于您的查询。
或者,使用动态 SQL。从字面上看,根据填充的参数编写不同的 SQL 语句。像这样
...
is
stmt varchar2(32767);
begin
stmt := '
select col1, col2, col3,col4,col5,col6
from master_table
where 1=1';
if p_field1 is not null then
stmt := stmt || ' and col1 = '''|| p_field1 ||'''';
end if;
if p_field2 is not null then
stmt := stmt || ' and col2 = '''|| p_field2 ||'''';
end if;
if p_field3 is not null then
stmt := stmt || ' and col3 = '''|| p_field3 ||'''';
end if;
open cur for stmt;
Run Code Online (Sandbox Code Playgroud)
注意:在上面的代码中我选择不使用绑定变量。我这样做的主要原因是需要在 OPEN .., USING 语句中对参数进行 64 种排列。然而,@WilliamRobertson 建议了一个 Tom Kyte 文章的链接,该文章有一个巧妙的方法来处理这个问题。了解更多。
这是我按照 Tom Kyte 的思路编写的代码:
...
is
stmt varchar2(32767);
begin
stmt := '
select col1, col2, col3,col4,col5,col6
from master_table';
if p_field1 is not null then
stmt := stmt || ' where col1 = :p_field1 ';
else
stmt := stmt || ' where (1 = 1 or :p_field1 is null)';
end if;
if p_field2 is not null then
stmt := stmt || ' and col2 =:p_field2 ';
else
stmt := stmt || ' and (1 = 1 or :p_field2 is null)';
end if;
if p_field3 is not null then
stmt := stmt || ' and col3 = :p_field3 ';
else
stmt := stmt || ' and (1 = 1 or :p_field3 is null)';
end if;
open cur for stmt
using p_field1, p_field2, p_field3;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
217 次 |
| 最近记录: |