[英]Oracle SQL - JOIN performance in comparing null values
早上好,
在昨天我在兩個體面大小的結果集(每個結果小於5萬個結果)之間編寫的查詢中,我的JOIN的一部分是一個子句,用於檢查數據是否匹配或為空(下面是簡化版):
SELECT a JOIN b ON a.class = b.class OR (a.class is null AND b.class is null)
但是,我注意到一個嚴重的性能問題,該問題集中在OR語句的使用上。 我使用以下方法解決了這個問題:
SELECT a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A')
第一個查詢的運行時間長得令人無法接受,而第二個查詢則快了幾個數量級(> 45分鍾vs. <1)。 我希望由於進行更多的比較而使OR的運行速度變慢,但是在此特定數據集中,a.class = b.class = null的情況相對較少。
是什么導致如此顯着的演奏時間增加? Oracle SQL不會像許多其他語言一樣短路布爾比較嗎? 有沒有辦法挽救第一個查詢而不是第二個查詢(不僅在Oracle中用於通用SQL)?
您將返回帶有任何具有空類的記錄的交叉乘積。 您的結果可以嗎?
我在11gR2中創建了兩個示例查詢:
WITH a as
(select NULL as class, 5 as columna from dual
UNION
select NULL as class, 7 as columna from dual
UNION
select NULL as class, 9 as columna from dual
UNION
select 'X' as class, 3 as columna from dual
UNION
select 'Y' as class, 2 as columna from dual),
b as
(select NULL as class, 2 as columnb from dual
UNION
select NULL as class, 15 as columnb from dual
UNION
select NULL as class, 5 as columnb from dual
UNION
select 'X' as class, 7 as columnb from dual
UNION
select 'Y' as class, 9 as columnb from dual)
SELECT * from a JOIN b ON (a.class = b.class
OR (a.class is null AND b.class is null))
當我對此查詢運行EXPLAIN PLAN時,它指示表(在我的情況下為內聯視圖)是通過NESTED LOOPS連接的。 NESTED LOOPS聯接的操作方式是:掃描一個表的第一行,然后掃描另一表的每一行以查找匹配項,然后掃描第一張表的第二行,在第二張表上查找匹配項,依此類推。因為您沒有直接比較在JOIN的OR部分的任何一個表中,優化器必須使用NESTED LOOPS。
在幕后看起來可能像這樣:
當我將SELECT語句更改為SELECT * FROM a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A')
,EXPLAIN表示使用了HASH JOIN。 哈希聯接實質上是為較小表的每個聯接鍵生成一個哈希,然后掃描較大表,在較小表中為匹配的每一行查找哈希。 在這種情況下,由於它是簡單的Equijoin,因此優化程序可以對驅動表的每一行進行哈希處理而不會出現問題。
在幕后看起來可能像這樣:
如果對查詢運行一個EXPLAIN PLAN,您可能會發現類似的結果。
即使最終結果是相同的,由於您沒有在第一個查詢中使用“ OR”來聯接表,因此優化器無法使用更好的聯接方法。 如果驅動表很大,或者您要對較大的輔助表強制進行全表掃描,則嵌套循環可能會非常慢。
您可以使用ANSI COALESCE
函數在其他數據庫系統中模擬NVL oracle函數。 真正的問題是,您嘗試加入一個NULL值,您實際上應該擁有一個“ NO CLASS”或某種其他方法來識別“ null”類,即null =無,而不是null =未知。
在評論中回答您的問題的附錄:
對於null查詢,SQL引擎將執行以下操作:
對於“ N / A”查詢,SQL引擎將執行以下操作:
在第一種情況下,因為每個空不同的是,數據庫不使用優化(每一行從a
支票,從表的每一行b
)。
在第二種情況下,數據庫首先將所有null更改為'N / A',然后僅使用優化將a.class
和b.class
進行比較
在Oracle中比較null非常耗時。 空是未定義的值-一個空與另一個空不同。 比較兩個幾乎相同的查詢的結果:
select 1 from dual where null is null
select 1 from dual where null = null
只有第一個特殊查詢is null
子句才能返回正確答案。 因此,不能為空值建立索引。
試試這個:
SELECT a from Table1 a JOIN JTable1 b ON a.class = b.class
where a.class is null
union all
SELECT a from Table1 a JOIN JTable1 b ON a.class = b.class
where b.class is null
應該快一點
解釋很簡單:第一個必須在聯接操作中使用嵌套循環,當您使用OR運算時總是會發生這種情況。 第二個必須使用哈希連接操作,該操作比上一個更快。
為什么不讓它變得容易些。 喜歡
選擇*從a,b那里a.class(+)= b.class(+)
我認為它更具可讀性。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.