2013-04-25 54 views
4

我最近在oracle數據庫中移動了一段代碼,其中一位經驗豐富的開發人員提到我有太多的existsnot exists陳述和應該有一種方法可以將它們刪除,但是由於他不得不使用它並且不太記得它的工作原理,所以它已經太久了。目前,我回過頭來讓代碼更易於維護,因爲隨着業務邏輯/需求的變化,這部分代碼可能會在未來幾年內多次更改,並且我希望繼續優化它,同時使它更易於維護。通過刪除'存在'和'不存在'來優化Oracle查詢

我嘗試過查找它,但是我可以找到的所有建議都是用not exists代替not in,並且不返回實際結果。

因此,我想知道什麼可以做,以優化出exists/not exists或是否有寫exists/not exists一種方式,以便(比我可能可以在一個更好的程度),甲骨文將在內部優化。

例如,如何優化以下內容?

UPDATE 
    SCOTT.TABLE_N N 
SET 
    N.VALUE_1 = 'Data!' 
WHERE 
    N.VALUE_2 = 'Y' 
    AND 
    EXISTS 
    (
     SELECT 
      1 
     FROM 
      SCOTT.TABLE_Q Q 
     WHERE 
      N.ID = Q.N_ID 
    ) 
    AND 
    NOT EXISTS 
    (
     SELECT 
      1 
     FROM 
      SCOTT.TABLE_W W 
     WHERE 
      N.ID = W.N_ID 
    ) 
+0

有沒有機會,「更有經驗的開發人員」對MySQL更有經驗? (在MySQL中,使用select的左外連接...其中null通常比not exists子句執行得更好,但在Oracle和許多其他數據庫中,not exists子句的性能通常會更好。) – 2013-04-25 15:43:37

+0

據我所知,他只是PL/SQL,並且提到Steven Feuerstein的Oracle PL/SQL編程作爲他閱讀優化的來源。他有可能錯誤地記住了不同的優化;人類確實有一個已知的錯誤,其中記憶可能部分錯誤。 – Lawtonfogle 2013-04-25 16:03:27

+0

Google書籍似乎總是在您提到的書中顯示此頁面:http://books.google.co.uk/books?id=vUbJkdl6vIcC&pg=PA848&dq="Oracle+PL/SQL+Programming"+"Steven+Feuerstein 「+」不+ +存在「 - 這不直接相關。然而,這個網頁:http://dbpedias.com/wiki/Oracle:Coding_Best_Practices_-_Using_EXISTS/NOT_EXISTS(這似乎是由Feuerstein先生撰寫的)基本上與現在的答案大致相同。最後,'減號'有時可能勝過'不存在' - 請參閱http://tech.dir.groups.yahoo.com/group/toad/message/23271?source=1&var=1。 – 2013-04-25 16:22:49

回答

7

你的聲明似乎完全沒有給我。

在任何優化任務中,都不要考慮模式。不要這樣想,「(not) exists不好,慢,(not) in是超級酷和快速」。

想想,數據庫在每個步驟上做了多少工作,以及如何測量它?

一個簡單的例子:

- NOT IN:

23:59:41 [email protected]> alter system flush buffer_cache; 

System altered. 

Elapsed: 00:00:00.03 
23:59:43 [email protected]> set autotrace traceonly explain statistics 
23:59:49 [email protected]> select country_id from countries where country_id not in (select country_id from locations); 

11 rows selected. 

Elapsed: 00:00:00.02 

Execution Plan 
---------------------------------------------------------- 
Plan hash value: 1748518851 

------------------------------------------------------------------------------------------ 
| Id | Operation    | Name   | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------------ 
| 0 | SELECT STATEMENT  |     |  1 |  6 |  4 (0)| 00:00:01 | 
|* 1 | FILTER    |     |  |  |   |   | 
| 2 | NESTED LOOPS ANTI SNA|     | 11 | 66 |  4 (75)| 00:00:01 | 
| 3 | INDEX FULL SCAN  | COUNTRY_C_ID_PK | 25 | 75 |  1 (0)| 00:00:01 | 
|* 4 | INDEX RANGE SCAN | LOC_COUNTRY_IX | 13 | 39 |  0 (0)| 00:00:01 | 
|* 5 | TABLE ACCESS FULL | LOCATIONS  |  1 |  3 |  3 (0)| 00:00:01 | 
------------------------------------------------------------------------------------------ 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    1 - filter(NOT EXISTS (SELECT 0 FROM "LOCATIONS" "LOCATIONS" WHERE 
       "COUNTRY_ID" IS NULL)) 
    4 - access("COUNTRY_ID"="COUNTRY_ID") 
    5 - filter("COUNTRY_ID" IS NULL) 


Statistics 
---------------------------------------------------------- 
      0 recursive calls 
      0 db block gets 
     11 consistent gets 
      8 physical reads 
      0 redo size 
     446 bytes sent via SQL*Net to client 
     363 bytes received via SQL*Net from client 
      2 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
     11 rows processed 

- NOT EXISTS

23:59:57 [email protected]> alter system flush buffer_cache; 

System altered. 

Elapsed: 00:00:00.17 
00:00:02 [email protected]> select country_id from countries c where not exists (select 1 from locations l where l.country_id = c.country_id); 

11 rows selected. 

Elapsed: 00:00:00.30 

Execution Plan 
---------------------------------------------------------- 
Plan hash value: 840074837 

------------------------------------------------------------------------------------- 
| Id | Operation   | Name   | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |     | 11 | 66 |  1 (0)| 00:00:01 | 
| 1 | NESTED LOOPS ANTI|     | 11 | 66 |  1 (0)| 00:00:01 | 
| 2 | INDEX FULL SCAN | COUNTRY_C_ID_PK | 25 | 75 |  1 (0)| 00:00:01 | 
|* 3 | INDEX RANGE SCAN| LOC_COUNTRY_IX | 13 | 39 |  0 (0)| 00:00:01 | 
------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    3 - access("L"."COUNTRY_ID"="C"."COUNTRY_ID") 


Statistics 
---------------------------------------------------------- 
      0 recursive calls 
      0 db block gets 
      5 consistent gets 
      2 physical reads 
      0 redo size 
     446 bytes sent via SQL*Net to client 
     363 bytes received via SQL*Net from client 
      2 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
     11 rows processed 

不限於在本示例讀取兩倍的數據庫塊並執行更復雜的過濾 - 問問自己,爲什麼你會選擇它而不是存在?

+0

+1優秀的答案。 – 2013-04-25 16:17:10

+1

我應該怎樣做每個時間,因爲它看起來'不在'需要'不存在'時間的1/15。或者我只是誤讀數據? – Lawtonfogle 2013-04-25 17:41:10

+0

@Lawtonfogle你的意思是,我的例子是0.02 vs 0.3?這基本上是一個掛鐘時間,它包括在我的sql * plus窗口中顯示輸出所用的時間,它可能會有所不同。我剛剛重新運行了這些查詢,碰巧「不存在」爲0.03,而「不在」爲0.07。如果你想準確計時,你應該檢查sql trace或者可能在v $ sql_plan_statistics中的某處 – 2013-04-25 17:48:30

2

當你需要時,沒有理由避免使用EXISTS或NOT EXISTS。在你給出的例子中,這可能正是你想要使用的。

典型的困境是使用IN/NOT IN還是EXISTS/NOT EXISTS。它們的評估方式差異很大,根據具體情況可能會更快或更慢。

請參閱here瞭解更多詳情,而不是您可能想要的。

1

我不知道,如果它的速度要快得多,但這裏是寫出來的方式,而不EXISTS/NOT EXISTS

MERGE INTO TABLE_N T 
USING (
    SELECT N.ID, 'Data!' AS NEW_VALUE_1 
    FROM SCOTT.TABLE_N N 
    INNER JOIN SCOTT.TABLE_Q Q 
     ON Q.N_ID = N.ID 
    LEFT JOIN SCOTT.TABLE_W W 
     ON W.N_ID = N.ID 
    WHERE N.VALUE_2 = 'Y' 
    AND W.ID IS NULL 
) X 
ON (T.ID = X.ID) 
WHEN MATCHED THEN UPDATE 
    SET T.VALUE_1 = X.NEW_VALUE_1;