我正在開發一些基於夜間的批處理作業,其中我使用rowid來查找表中的記錄,但總是回退到某些邏輯鍵或某些東西,如果rowid不通過。ORACLE 9i PLSQL優化器導致與常量VARIABLES的一些性能問題
我開發了一個小測試用例,向您展示我的問題,並提供您可以在自己的環境中複製的內容。
創建表,並把裏面的
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。 如果它不爲空,那麼在該查詢的所有測試用例中,第一次不會爲空...我明顯是錯的
非常感謝。
ROWID不保證在事務之外保持不變。數據庫可以隨意移動行,因此會改變ROWID。 ROWID不能代替主鍵。分享並享受。 –