检查 WHERE 子句中参数是否为 NULL
我在执行一个存储过程时遇到了麻烦,该过程需要永远执行。它相当大,我可以理解我需要一些时间,但这个持续了将近 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)进行了全表扫描。关于如何避免这种情况的任何想法?
I´m having trouble with a Stored Procedure that takes about forever to execute. It is quite large and I can understand that I´ll take some time but this one continues for almost 20 minutes.
After some debugging and researching I noticed that replacing this part of the WHERE
clause;
((p_DrumNo IS NULL) OR T_ORDER.ORDER_ID IN (SELECT ORDER_ID FROM ORDERDELIVERY))
made a HUGE difference. So the Procedure works just fine as long as p_DrumNo is NULL or I modify the above to not check if p_DrumNo is NULL;
(T_ORDER.ORDER_ID IN (SELECT ORDER_ID FROM ORDERDELIVERY))
The goal with this WHERE
clause is to filter the result set on p_DrumNo if it´s passed in to the Stored Procedure. The WHERE
clause then continues with further conditions but this specific one halts the query.
ORDERDELIVERY is just a ~temporary table containing ORDER_IDs related to the parameter p_DrumNo.
How can this simple IS NULL-check cause such big impact? It´s probably related to the use of OR
together with the subquery but I don´t understand why as the subquery itself works just fine.
Thanks in advance!
UPDATE [2011-09-23 10:13]
I´ve broken down the problem into this small query that show the same behaviour;
Example A
SQL query
SELECT * FROM T_ORDER WHERE
('290427' IS NULL OR ORDER_ID IN (SELECT ORDER_ID FROM T_ORDER WHERE ORDERNO LIKE '290427%') );
Execution plan
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
Example B
SQL query
SELECT * FROM T_ORDER WHERE
( ORDER_ID IN (SELECT ORDER_ID FROM T_ORDER WHERE ORDERNO LIKE '290427%') );
Execution plan
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
As you all can see the first query (example A) makes a full table scan. Any ideas on how I can avoid this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不要在 SQL 语句本身中评估过程的参数状态,而是将该评估移至包含的 PL/SQL 块,以便在提交理想的 SQL 语句之前仅执行一次。例如:
我在使用
OR
调整 SQL 语句方面也取得了一些成功,方法是使用 UNION ALL 将语句分成两个互斥的语句:Instead of evaluating your procedure's parameter state in the SQL statement itself, move that evaulation to the containing PL/SQL block so it's executed only once before the ideal SQL statement is submitted. For example:
I've also had some success in tuning SQL statements with an
OR
by breaking the statement into two mutually exclusive statements with a UNION ALL:您遇到了这样的问题,因为如果您在查询中包含
OR
,您的索引将无法工作。为了获得相同的信息,我宁愿这样做以使索引工作(基于更新的查询):它将返回相同的结果,因为
290427
似乎是一个变量并且它往往为 null 或在特定时刻不为空。但您也可以尝试在存储的内部使用 动态 sql用于此类目的的程序:
我想说我没有看到
IN
的意义,它是完全相同的:You came across such an issue due to the fact your index doesn't work if you include
OR
to your query. To get the same info I'd rather do this to make the index work (basing on updated query):It will return the same result since
290427
seem to be a variable and it tends to be null or not null at the particular moment.But also you can try using dynamic sql inside you stored procedure for such purposes:
And I wanted to say that I don't see sence of that
IN
, it'd be quite the same: