2014-04-08 38 views
2

我正在開發一些基於夜間的批處理作業,其中我使用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。 如果它不爲空,那麼在該查詢的所有測試用例中,第一次不會爲空...我明顯是錯的

非常感謝。

+0

ROWID不保證在事務之外保持不變。數據庫可以隨意移動行,因此會改變ROWID。 ROWID不能代替主鍵。分享並享受。 –

回答

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的第二個一半。

1

爲什麼你甚至需要在SQL代碼中包含w_rowid?你事先知道,如果它爲空或不是,所以葫蘆出來的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; 
+0

我明白你在說什麼,但產生這個問題的奇瑞是一個有4個連接和50列左右的大型查詢來分析。 – JGS