簡體   English   中英

Oracle SQL-比較空值的JOIN性能

[英]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。

在幕后看起來可能像這樣:

  • 獲取表A的行1。如果class為null,則將表A的這一行包括在結果集中。
  • 仍在表A第1行上時,在表B中搜索class為null的所有行。
  • 對表A第1行和表B中的所有行執行叉積
  • 將這些行包括在結果集中
  • 獲取表A的第2行。如果class為null,則將表A的這一行包括在結果集中。
  • ....等

當我將SELECT語句更改為SELECT * FROM a JOIN b ON NVL(a.class, 'N/A') = NVL(b.class, 'N/A') ,EXPLAIN表示使用了HASH JOIN。 哈希聯接實質上是為較小表的每個聯接鍵生成一個哈希,然后掃描較大表,在較小表中為匹配的每一行查找哈希。 在這種情況下,由於它是簡單的Equijoin,因此優化程序可以對驅動表的每一行進行哈希處理而不會出現問題。

在幕后看起來可能像這樣:

  • 遍歷表A,將NULL類值轉換為“ N / A”
  • 哈希表A的每一行。
  • 哈希表A現在處於臨時空間或內存中。
  • 掃描表B,將NULL類值轉換為“ N / A”,然后計算值的哈希值。 哈希表中的查找哈希(如果存在)將來自表A和B的聯接行包含在結果集中。
  • 繼續掃描B。

如果對查詢運行一個EXPLAIN PLAN,您可能會發現類似的結果。

即使最終結果是相同的,由於您沒有在第一個查詢中使用“ OR”來聯接表,因此優化器無法使用更好的聯接方法。 如果驅動表很大,或者您要對較大的輔助表強制進行全表掃描,則嵌套循環可能會非常慢。

您可以使用ANSI COALESCE函數在其他數據庫系統中模擬NVL oracle函數。 真正的問題是,您嘗試加入一個NULL值,您實際上應該擁有一個“ NO CLASS”或某種其他方法來識別“ null”類,即null =無,而不是null =未知。

在評論中回答您的問題的附錄:

對於null查詢,SQL引擎將執行以下操作:

  1. 從表A中讀取第1行,類為空,轉換為“ N / A”。
  2. 表B具有3個類別為nu​​ll的行,請將每個null轉換為“ N / A”。
  3. 由於第一行與所有3行都匹配,因此將3行添加到我們的結果集中,其中A1B1,A1B2,A1B3對應一行。
  4. 從表A讀取第2行,類為null,轉換為'N / A'/
  5. 表B具有3個類別為nu​​ll的行,請將每個null轉換為“ N / A”。
  6. 由於第二行與所有3行都匹配,因此將3行添加到我們的結果集中,其中1行用於A2B1,A2B2,A2B3。
  7. 從表A讀取第3行,類為null,轉換為'N / A'/
  8. 表B具有3個類別為nu​​ll的行,請將每個null轉換為“ N / A”。
  9. 由於第三行與所有3行都匹配,因此將3行添加到我們的結果集中,其中A3B1,A3B2,A3B3對應一行。 10 ..第4行和第5行不為null,因此在連接的這一部分中將不會對其進行處理。

對於“ N / A”查詢,SQL引擎將執行以下操作:

  1. 從表A中讀取第1行,類為空,轉換為“ N / A”,對該值進行哈希處理。
  2. 從表A中讀取第2行,類為null,轉換為“ N / A”,對該值進行哈希處理。
  3. 從表A中讀取第3行,類為空,轉換為“ N / A”,對該值進行哈希處理。
  4. 從表A中讀取第4行,類不為null,對該值進行哈希處理。
  5. 從表A中讀取第5行,類不為null,對該值進行哈希處理。
  6. 哈希表C現在在內存中。
  7. 從表B中讀取第1行,類為null,轉換為'N / A',對該值進行哈希處理。
  8. 將哈希值與內存中的哈希表進行比較,為每個匹配項在結果集中添加一行。 找到3行A1,A2和A3。 將結果添加到A1B1,A2B1,A3B1。
  9. 從表B中讀取第2行,類為null,轉換為'N / A',對該值進行哈希處理。
  10. 將哈希值與內存中的哈希表進行比較,為每個匹配項在結果集中添加一行。 找到3行A1,A2和A3。 將結果添加到A1B2,A2B2,A3B2。
  11. 從表B中讀取第3行,類為空,轉換為“ N / A”,對值進行哈希處理。
  12. 將哈希值與內存中的哈希表進行比較,為每個匹配項在結果集中添加一行。 找到3行A1,A2和A3。 將結果添加到A1B3,A2B3,A3B3。

在第一種情況下,因為每個空不同的是,數據庫不使用優化(每一行從a支票,從表的每一行b )。

在第二種情況下,數據庫首先將所有null更改為'N / A',然后僅使用優化將a.classb.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM