我遇到了一个需要永久执行的存储过程。这是非常大的,我可以理解,我会花一些时间,但这一次持续将近20分钟。
经过一些调试和研究后,我注意到替换了WHERE
子句的这一部分;
((p_DrumNo IS NULL) OR T_ORDER.ORDER_ID IN (SELECT ORDER_ID FROM ORDERDELIVERY))
取得了巨大的成功。所以只要p_DrumNo为NULL,或者我修改上面的内容就不检查p_DrumNo是否为NULL,程序就可以正常工作;
(T_ORDER.ORDER_ID IN (SELECT ORDER_ID FROM ORDERDELIVERY))
此WHERE
子句的目标是在p_DrumNo上传递结果集,如果它传入存储过程。然后WHERE
子句继续进一步的条件,但是这个特定的条件会暂停查询。
ORDERDELIVERY只是一个包含与参数p_DrumNo相关的ORDER_ID的临时表。
这个简单的IS NULL检查怎么会造成如此大的影响?这可能与OR
和子查询的使用有关,但我不明白为什么因为子查询本身工作得很好。
提前致谢!
更新 [2011-09-23 10:13]
我已将问题分解为显示相同行为的小型查询;
示例A
SQL查询
SELECT * FROM T_ORDER WHERE
('290427' IS NULL OR ORDER_ID IN (SELECT ORDER_ID FROM T_ORDER WHERE ORDERNO LIKE '290427%') );
执行计划
OPERATION OBJECT_NAME OPTIONS COST
------------------------------------------------------------
SELECT STATEMENT 97
FILTER
TABLE ACCESS T_ORDER FULL 95
TABLE ACCESS T_ORDER BY INDEX ROWID 2
INDEX PK_ORDER UNIQUE SCAN 1
示例B
SQL查询
SELECT * FROM T_ORDER WHERE
( ORDER_ID IN (SELECT ORDER_ID FROM T_ORDER WHERE ORDERNO LIKE '290427%') );
执行计划
OPERATION OBJECT_NAME OPTIONS COST
------------------------------------------------------------
SELECT STATEMENT 4
NESTED LOOPS 4
TABLE ACCESS T_ORDER BY INDEX ROWID 3
INDEX IX_T_ORDER_ORDERNO RANGE SCAN 2
TABLE ACCESS T_ORDER BY INDEX ROWID 1
INDEX PK_ORDER UNIQUE SCAN 0
大家可以看到第一个查询(示例A)进行全表扫描。关于如何避免这种情况的任何想法?
答案 0 :(得分:4)
不是在SQL语句本身中评估过程的参数状态,而是将该evaulation移动到包含的PL / SQL块,以便在提交理想的SQL语句之前只执行一次。例如:
CREATE OR REPLACE PROCEDURE my_sp (p_DrumNo VARCHAR2)
IS
BEGIN
IF p_DrumNo IS NULL THEN
SELECT ...
INTO ... -- Assumed
FROM ...
WHERE my_column = p_DrumNo;
ELSE
SELECT ...
INTO ... -- Assumed
FROM ...
WHERE ORDER_ID IN (SELECT ORDER_ID FROM ORDERDELIVERY);
END;
END;
通过使用UNION ALL将语句分成两个互斥语句,我在使用OR
调优SQL语句方面也取得了一些成功:
SELECT ...
FROM ...
WHERE p_DrumNo IS NULL
AND ORDER_ID IN (SELECT ORDER_ID FROM ORDERDELIVERY)
UNION ALL
SELECT ...
FROM ...
WHERE p_DrumNo IS NOT NULL
AND my_column = p_DrumNo;
答案 1 :(得分:3)
您遇到了这样的问题,因为如果您在查询中包含OR
,则索引不起作用。为了获得相同的信息,我宁愿这样做以使索引工作(基于更新的查询):
SELECT * FROM T_ORDER WHERE '290427' IS NULL
UNION ALL
SELECT * FROM T_ORDER WHERE ORDER_ID IN (SELECT ORDER_ID FROM T_ORDER WHERE ORDERNO LIKE '290427%'));
它将返回相同的结果,因为290427
似乎是一个变量,并且在特定时刻它往往为null或不为null。
但您也可以尝试在存储过程中使用dynamic sql来实现此目的:
%begin_of_the_procedure%
query_ := 'SELECT * FROM T_ORDER WHERE 1=1';
if var_ is not null then
query_ := query_||' AND ORDER_ID IN (SELECT ORDER_ID FROM T_ORDER WHERE ORDERNO LIKE '''||to_char(var_)||'%'')';
end if;
open cursor_ query_;
%fetching cursor loop%
%end_of_the_procedure%
我想说我看不到那个IN
,它是完全一样的:
SELECT * FROM T_ORDER WHERE ('290427' IS NULL OR ORDERNO LIKE '290427%');