存储过程SQL执行计划

时间:2015-04-14 14:42:28

标签: sql oracle sql-execution-plan

我对执行速度非常慢的存储过程感到有些困惑。存储过程基本上包含一个使用传入参数(in_id)的查询,并放在这样的游标中:

open tmp_cursor for 
select col1, col2, col3
from table1 tab
where ((in_id is null) or (tab.id = in_id));  -- tab.id is the PK

当我使用预定义值单独获得SQL查询的执行计划时,使用索引查询得到了很好的结果。但是,当我从我的应用程序调用该过程时,我发现没有使用索引并且表格被完全扫描,从而导致性能降低。
如果我删除WHERE子句的第一部分"(in_id为null)"应用程序的性能再次快速 为什么索引在我的应用程序调用期间没有被使用(传入in_id)?

3 个答案:

答案 0 :(得分:2)

  

in_id为null

我在这里回答了类似的问题https://stackoverflow.com/a/26633820/3989608

有关NULL值和INDEX的一些事实:

  • 完整的NULL键未输入Oracle中的“普通”B *树

  • 因此,如果您在C1和C2上有连锁索引,那么您可能会在其中找到NULL值 - 因为您可能有一行其中C1为NULL但C2为非NULL - 该键值将为在索引中。

Thomas Kyte关于同样的演示的一部分:

ops$tkyte@ORA9IR2> create table t
2  as
3  select object_id, owner, object_name
4    from dba_objects;
Table created.

ops$tkyte@ORA9IR2> alter table t modify (owner NOT NULL);
Table altered.

ops$tkyte@ORA9IR2> create index t_idx on t(object_id,owner);
Index created.

ops$tkyte@ORA9IR2> desc t
Name                    Null?    Type
----------------------- -------- ----------------
OBJECT_ID                        NUMBER
OWNER                   NOT NULL VARCHAR2(30)
OBJECT_NAME                      VARCHAR2(128)

ops$tkyte@ORA9IR2> exec dbms_stats.gather_table_stats(user,'T');
PL/SQL procedure successfully completed.

那么,当应用于OBJECT_ID时,该索引当然可以用来满足“IS NOT NULL”:

ops$tkyte@ORA9IR2> set autotrace traceonly explain
ops$tkyte@ORA9IR2> select * from t where object_id is null;

Execution Plan
----------------------------------------------------------
0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=34)
1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=3 Card=1 Bytes=34)
2    1     INDEX (RANGE SCAN) OF 'T_IDX' (NON-UNIQUE) (Cost=2 Card=1)

实际上 - 即使表没有任何NOT NULL列,或者我们不希望/需要有一个涉及OWNER的连接索引 - 有一种透明的方法可以很容易地找到NULL OBJECT_ID值:

ops$tkyte@ORA9IR2> drop index t_idx;
Index dropped.

ops$tkyte@ORA9IR2> create index t_idx_new on t(object_id,0);
Index created.

ops$tkyte@ORA9IR2> set autotrace traceonly explain
ops$tkyte@ORA9IR2> select * from t where object_id is null;

Execution Plan
----------------------------------------------------------
0      SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=34)
1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=3 Card=1 Bytes=34)
2    1     INDEX (RANGE SCAN) OF 'T_IDX_NEW' (NON-UNIQUE) (Cost=2 Card=1)

来源:Something about nothing by Thomas Kyte

答案 1 :(得分:1)

假设in_id是查询参数 - 而不是列名:

无论输入如何,查询都必须只有一个exec计划。因此,如果将参数in_id作为NULL传递,则应该返回所有行。如果传递非NULL in_id则应该只返回一个PK值。

所以Oracle选择了最差的"#34; EXEC。计划处理"最坏的可能"场景。 "泛型"查询是通往地狱的道路。只需将查询拆分为两个。

select col1, col2, col3
from table1 tab
where in_id is null or in_id is not null;

这将使用FULL表扫描,这是获取所有行的最佳方法。

select col1, col2, col3
from table1 tab
where tab.id = in_id;  -- tab.id is the PK

这将使用UNIQUE索引扫描,这是获取单个索引行的最佳方法。

答案 2 :(得分:0)

select col1, col2, col3 from table1 tab where (tab.id = nvl(in_id,tab.id));

可能有帮助......或者您可以使用oracle提示

+Use_concat