【发布时间】:2014-05-21 15:27:55
【问题描述】:
我正在开发一些基于夜间的批处理作业,在这些作业中,我使用 rowids 来定位表中的记录,但如果 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;
我的执行时间几乎为零。 (???)
我发现问题主要是检查是否为 null,如果我删除/注释该代码,我的时间也接近于零......
最后的sn-p代码在这里:
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。 如果第一次不为空,则在该查询的所有测试用例中都不为空......我显然错了
非常感谢。
【问题讨论】:
-
ROWID 不能保证在事务之外保持不变。数据库可以随意移动行,因此会更改 ROWID。 ROWID 不能替代主键。分享和享受。
标签: sql performance oracle plsql oracle9i