我执行以下程序:
DECLARE
PROCEDURE my_proc(arg1 IN NUMBER,
arg2 IN NUMBER)
IS
ltimestamp_start timestamp;
ltimestamp_stop timestamp;
linterval_diff interval day to second;
BEGIN
ltimestamp_start := systimestamp;
UPDATE transfers t1
SET t1.trn_valid = 'Y'
WHERE EXISTS(
SELECT 1
FROM transfers t2
JOIN tasks u1 ON trn_tsk_id = u1.tsk_id
WHERE t1.trn_id = t2.trn_id
AND t2.trn_pom_id = arg1
AND u1.tsk_type <> 'TT'
AND (
(arg2 IS NULL AND t2.trn_ord IN ('W', 'K') AND t2.trn_type <> 'P' AND t2.trn_mng IN ('0', '1')) OR
(arg2 IS NOT NULL AND t2.trn_ord IN ('W', 'K') AND t2.trn_type <> 'P' AND arg2 = t2.trn_id)
)
);
ltimestamp_stop := systimestamp;
linterval_diff := ltimestamp_stop - ltimestamp_start;
DBMS_OUTPUT.PUT_LINE(EXTRACT(SECOND FROM linterval_diff));
END;
BEGIN
my_proc(200, 300);
END;
平均而言,它需要0,085939。
但是,如果我发表评论:
--(arg2 IS NULL AND t2.trn_ord IN ('W', 'K') AND t2.trn_type <> 'P' AND t2.trn_mng IN ('0', '1')) OR
执行只需要0,000275s。
在这种情况下,arg2等于300,但检查条件&#34; arg2 IS NULL&#34;大大减慢了程序。
为什么我会得到如此奇怪的结果?
答案 0 :(得分:2)
当您注释掉(arg2 IS NULL...
时,您只留下t2.trn_id = arg2
的强制条件,为Oracle的优化工具提供了非常明确的访问路径(假设t2.trn_id
比t2.trn_pom_id
)。
Oracle的优化器有很多技巧可以将OR
个查询转换为多个部分,每个部分都以不同的方式访问数据并且运行良好。在这种情况下,甲骨文的伎俩可能会失败。
您可以尝试通过拆分它的逻辑来帮助CBO。我相信您的查询等同于以下内容,Oracle的CBO可能会有更好的时间:
UPDATE transfers t1
SET t1.trn_valid = 'Y'
WHERE EXISTS(
SELECT 1
FROM transfers t2
JOIN tasks u1 ON trn_tsk_id = u1.tsk_id
WHERE t1.trn_id = t2.trn_id
AND t2.trn_pom_id = arg1
AND u1.tsk_type <> 'TT'
AND t2.trn_ord IN ('W', 'K')
AND t2.trn_type <> 'P'
AND t2.trn_mng IN ('0', '1')
AND arg2 IS NULL
UNION ALL
SELECT 1
FROM transfers t2
JOIN tasks u1 ON trn_tsk_id = u1.tsk_id
WHERE t1.trn_id = t2.trn_id
AND t2.trn_pom_id = arg1
AND u1.tsk_type <> 'TT'
AND t2.trn_ord IN ('W', 'K')
AND t2.trn_type <> 'P'
AND arg2 = t2.trn_id
AND arg2 IS NOT NULL
);