我正在开发一些基于夜间的批处理作业,其中我使用rowid来查找表中的记录,但是如果没有传递rowid,则总是会回退到某个逻辑键或其他东西。
我开发了一个小测试用例来向您展示我的问题,并提供可以在您自己的环境中复制的内容。
创建表并在
中放入一些数据create table table_with_4M_records (varchar_column varchar2(7));
begin
for i in 1..4000000
loop
insert into table_with_4M_records(varchar_column) values (''||lpad(i,7,0));
end loop;
end;
我发现的是,如果我运行类似的东西:
DECLARE
w_rowid constant VARCHAR2(30) := 'AAF6CCAAnAAAaz9AHX';
w_counter NUMBER;
w_t0 TIMESTAMP;
w_t1 TIMESTAMP;
w_time_diff_in_ms NUMBER:=null;
BEGIN
w_t0 := SYSTIMESTAMP;
SELECT count(*)
INTO w_counter
FROM table_with_4M_records
WHERE ((w_rowid IS NOT NULL AND ROWID = w_rowid )
OR
(w_rowid IS NULL /*Then do some heavy operations which I only want to do if the rowid comes with no value*/ )
);
w_t1 := SYSTIMESTAMP;
SELECT EXTRACT(DAY FROM diff )*24*60*60*1000 + --Days
EXTRACT(HOUR FROM diff )*60*60*1000 + --Hours
EXTRACT(MINUTE FROM diff )*60*1000 + --Minutes
round(EXTRACT(SECOND FROM diff )*1000)total_milliseconds --Seconds
INTO w_time_diff_in_ms
FROM (SELECT (w_t1-w_t0) diff FROM dual);
dbms_output.put_line('REC COUNTER: '||w_counter);
dbms_output.put_line('APROX EXEC TIME IN MILLISECS: '||w_time_diff_in_ms);
END;
我获得了几秒钟的获取时间,类似于5秒......奇怪吧?因为我使用的是ROWID,所以应该是直接获取。
但是如果我用代码替换代码中的w_rowid。像这样:
DECLARE
w_rowid constant VARCHAR2(30) := 'AAAGg5AAWAAAaffAA0';
w_counter NUMBER;
w_t0 TIMESTAMP;
w_t1 TIMESTAMP;
w_time_diff_in_ms NUMBER:=null;
BEGIN
w_t0 := SYSTIMESTAMP;
SELECT count(*)
INTO w_counter
FROM table_with_4M_records
WHERE (('AAF6CCAAnAAAaz9AHX' IS NOT NULL AND ROWID = 'AAF6CCAAnAAAaz9AHX' )
OR
('AAF6CCAAnAAAaz9AHX' IS NULL /*Then do some heavy operations witch I only want to do if the rowid comes with no value*/ )
);
w_t1 := SYSTIMESTAMP;
SELECT EXTRACT(DAY FROM diff )*24*60*60*1000 + --Days
EXTRACT(HOUR FROM diff )*60*60*1000 + --Hours
EXTRACT(MINUTE FROM diff )*60*1000 + --Minutes
round(EXTRACT(SECOND FROM diff )*1000)total_milliseconds --Seconds
INTO w_time_diff_in_ms
FROM (SELECT (w_t1-w_t0) diff FROM dual);
dbms_output.put_line('REC COUNTER: '||w_counter);
dbms_output.put_line('APROX EXEC TIME IN MILLISECS: '||w_time_diff_in_ms);
END;
我的执行时间几乎为零。 (???)
我发现问题主要是检查是否为空,如果我删除/评论该代码我也会接近零...
最后一段代码在这里:
DECLARE
w_rowid constant VARCHAR2(30) := 'AAF6CCAAnAAAaz9AHX';
w_counter NUMBER;
w_t0 TIMESTAMP;
w_t1 TIMESTAMP;
w_time_diff_in_ms NUMBER:=null;
BEGIN
w_t0 := SYSTIMESTAMP;
SELECT count(*)
INTO w_counter
FROM table_with_4M_records
WHERE ((w_rowid IS NOT NULL AND ROWID = w_rowid )
--OR
--(w_rowid IS NULL /*Then do some heavy operations witch I only want to do if the rowid comes with no value*/ )
);
w_t1 := SYSTIMESTAMP;
SELECT EXTRACT(DAY FROM diff )*24*60*60*1000 + --Days
EXTRACT(HOUR FROM diff )*60*60*1000 + --Hours
EXTRACT(MINUTE FROM diff )*60*1000 + --Minutes
round(EXTRACT(SECOND FROM diff )*1000)total_milliseconds --Seconds
INTO w_time_diff_in_ms
FROM (SELECT (w_t1-w_t0) diff FROM dual);
dbms_output.put_line('REC COUNTER: '||w_counter);
dbms_output.put_line('APROX EXEC TIME IN MILLISECS: '||w_time_diff_in_ms);
END;
如果你们中有任何人有任何好的建议来克服这个问题我真的很感激。 如果您有任何人可以使用不同版本的oracle在本地环境中测试此代码,我也不胜感激。
我目前正在运行Oracle 9.2I,我认为优化器更聪明,并且意识到我正在使用在查询中未更改的常量varchar2。 如果它不是null,那么第一次在该查询的所有测试用例中都不为null ......我显然是错误的
非常感谢。
答案 0 :(得分:1)
为什么甚至需要在SQL代码中包含w_rowid?您提前知道它是否为null,因此将其提升到PL / SQL代码并针对两种不同情况优化SQL查询(为清晰起见,删除一些代码):
DECLARE
w_rowid constant VARCHAR2(30) := 'AAF6CCAAnAAAaz9AHX';
w_counter NUMBER;
BEGIN
IF w_rowid IS NOT NULL THEN
SELECT count(*)
INTO w_counter
FROM table_with_4M_records
WHERE ROWID = w_rowid;
ELSE
/* do another select if w_rowid is null */
END IF;
END;
答案 1 :(得分:1)
在第一种情况下:
WHERE ((w_rowid IS NOT NULL AND ROWID = w_rowid ) OR
(w_rowid IS NULL /*Then do some heavy operations*/ )
);
查询优化器不会对OR
条件进行“延迟”评估。它会评估所有内容,包括您的“繁重操作”。认识到你将w_rowid
声明为常量是不够聪明的。
在第二种情况下:
WHERE (('AAF6CCAAnAAAaz9AHX' IS NOT NULL AND ROWID = 'AAF6CCAAnAAAaz9AHX' ) OR
('AAF6CCAAnAAAaz9AHX' IS NULL /*Then do some heavy operations*/ )
);
由于常量值,优化器可以在编译时简化表达式,因此忽略了OR
的后半部分。