2012-05-08 77 views
10

UNIONUNION ALL在某些情況下,使用OR連接的謂詞可以使查詢性能優於等效查詢。據我所知,這部分是因爲UNION子查詢可以並行執行,因此他們可以擁有與連接謂詞的每個部分相關的自己的「子計劃」,由於更簡單的適用查詢轉換,該子查詢可能更加優化。讓Oracle將OR連接謂詞轉換爲UNION ALL操作

但是,編寫OR - 即使將子查詢因子應用於UNION ALL解決方案,連接謂詞通常也更具可讀性和簡潔性。我的問題是:是否有辦法向Oracle表明,單個昂貴的OR謂詞應該轉換爲UNION ALL操作?如果有這樣的提示/方法,在什麼情況下可以應用(例如,謂詞所涉及的列中是否存在任何約束)?舉個例子:

CREATE TABLE a AS 
    SELECT 1 x, 2 y FROM DUAL UNION ALL 
    SELECT 2 x, 1 y FROM DUAL; 

-- This query... 
SELECT * FROM a 
WHERE x = 1 OR y = 1 

-- Is sometimes outperformed by this one, for more complex table sources... 
-- Note: in my case, I can safely apply UNION ALL. I know the two predicates to 
-- be mutually exclusive. 
SELECT * FROM a 
WHERE x = 1 
UNION ALL 
SELECT * FROM a 
WHERE y = 1 

注意,我知道了/*+ USE_CONCAT */暗示:

SELECT /*+ USE_CONCAT */ * FROM a 
WHERE x = 1 OR y = 1 

但它似乎並沒有產生我需要什麼(在執行計劃中沒有強制UNION ALL操作):

------------------------------------------- 
| Id | Operation   | Name | E-Rows | 
------------------------------------------- 
| 0 | SELECT STATEMENT |  |  | 
|* 1 | TABLE ACCESS FULL| A |  2 | 
------------------------------------------- 

也許,這個提示有一些限制嗎?我有Oracle 11g2可用於此。

+0

條件'x = 1或y = 1'會返回多少行(以所有行的百分比表示)?在「或」查詢中使用「PARALLEL」提示怎麼辦? –

+0

@a_horse_with_no_name:實際上,(實際)條件是'(flag_function()= 1和condition1)或(flag_function()= 0和condition2)'的形式。這兩個子條件根據PL/SQL'flag_function()'互相排斥。我注意到,在使用'UNION ALL'而不是使用'OR'時,Oracle爲此創建了一個更好的計劃。 'PARALLEL'可能沒有什麼幫助,因爲在這種情況下數據量並不是很大,但是計劃很複雜......此外,這個查詢在用戶會話中運行得非常頻繁。我不想用'PARALLEL'提示 –

+1

來處理太多資源。這些查詢並不等同。你應該使用UNION而不是UNION ALL。如果行和行的x和y都等於1,則會得到不同的結果。 – GriffeyDog

回答

3

我認爲這可能與您在OR謂詞中使用的列上存在的索引有關。

我在11gR2中使用以下測試。

create table scott.test as 
select level l, 
     decode(mod(level,2), 1, 1, 2) x, 
     decode(mod(level,2), 1, 2, 1) y, 
     dbms_random.value(1, 3) z from dual 
connect by level < 1000; 
/

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

我再解釋蟾蜍以下查詢,(EXPLAIN PLAN FOR

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4         


select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

所以它似乎暗示不工作。然後我添加一個索引到X & Y列:

create index test_x on test (x, y); 

begin 
    dbms_stats.gather_table_stats('scott', 'test'); 
end; 
/

現在重新運行查詢:

select x, y, z from scott.test 
    where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
    ; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   4         
    TABLE ACCESS FULL COS_DM.TEST 10  280  4 

select /*+ USE_CONCAT */ x, y, z from scott.test 
where (floor(z) = 1 and x = 1) or (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    CONCATENATION            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

select x, y, z from test where (floor(z) = 1 and x = 1) 
union all 
select x, y, z from test where (floor(z) = 2 and y = 1) 
; 

SELECT STATEMENT Optimizer Mode=ALL_ROWS  10   8         
    UNION-ALL            
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         
    TABLE ACCESS FULL COS_DM.TEST 5 140  4         

看來,添加索引(即使它不使用)優化後畢竟決定使用提示!

也許你可以試試這個?

+0

非常感謝您的分析!事實上,沒有索引(由「x」和「y」表示的真實列)。我會嘗試在'x'上添加一個索引。然而,'y'不能在真正的查詢中容易地索引,因爲它來源於'LEFT OUTER JOIN' ... –

+0

優化器對這些事情是挑剔的。您可能會發現它也可能嘗試將位圖轉換爲帶有位圖的rowid或者如果您獨立索引每列。 –

+0

好的,我明白了,我會盡力跟進這些事情。經過一番調查後發現,'CONCATENATION'真的被應用於真正的查詢中(我犯了一個錯誤)。但你仍然回答我簡化的問題,這似乎是過分簡化了... :) –