2016-11-11 49 views
8

任何人都可以解釋爲什麼以下查詢返回兩行而不是一行?可能帶有子查詢和組函數的Oracle-Bug

SELECT * 
    FROM (SELECT 'ASDF' c1, MAX (SUM (1)) c2 
      FROM DUAL 
     GROUP BY dummy 
     UNION 
     SELECT 'JKLÖ' c1, 1 c2 
      FROM DUAL) 
WHERE c1 != 'ASDF'; 

--another Version with the same wrong result: 
SELECT * 
    FROM (SELECT 1 c1, MAX (SUM (1)) c2 
      FROM DUAL 
     GROUP BY dummy 
     UNION all 
     SELECT 2 c1, 1 c2 
      FROM DUAL) 
WHERE c1 != 1; 

Oracle提供兩行是否正確?在我看來,c1 = ASDF的行不應該在結果中。

這是第一個查詢結果的截圖:

enter image description here

我已經測試了以下版本,總是以相同的結果:

  • Oracle數據庫11g企業版本11.2.0.3.0 - 64位生產
  • Oracle Database 12c企業版版本12.1.0.2.0 - 64位生產
+0

您能否包含結果集? –

+0

不通過啞元組造成語法錯誤? –

+0

包含結果集的屏幕截圖。不,沒有語法錯誤 - 我在真正的表格中也遇到了問題,我只是儘可能使樣本變得簡單和小巧。 –

回答

1

它絕對看起來像一個錯誤。

我真的不知道如何閱讀解釋計劃,但在這裏。在我看來,謂詞只被推送到UNION的其中一個成員,並且它已被轉換爲「NULL IS NOT NULL」,這非常奇怪。

請注意,可以將字符串更改爲'a'和'b'(所以我們不使用特殊字符),UNION和UNION ALL產生相同的錯誤,並且錯誤似乎由MAX SUM(1))在第一個分支中;簡單地用NULL或其他任何簡單的內容代替,甚至用SUM(1)(不包括MAX)都會導致查詢正常工作。

ADDED:奇怪的是,如果我改變MAX(SUM(1))要麼MAX(1)SUM(1),或者如果我只是將其更改爲文字數1,查詢工作正常 - 但解釋計劃仍然顯示了同樣怪異的謂語,「NULL不是NULL「。所以,問題似乎是謂詞不會被推到聯合的兩個分支,而不是謂詞轉換。 (甚至不能解釋爲什麼c2在結果集的額外行中顯示爲NULL)。更多新增(請參閱下面的註釋) - 事實證明,謂詞IS被推送到UNION的兩個分支,並且這正是導致問題的原因(正如尼古拉斯在他的回答中所解釋的)。

Plan hash value: 1682090214 

------------------------------------------------------------------------------- 
| Id | Operation    | Name | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT  |  |  2 | 32 |  2 (0)| 00:00:01 | 
| 1 | VIEW     |  |  2 | 32 |  2 (0)| 00:00:01 | 
| 2 | UNION-ALL   |  |  |  |   |   | 
| 3 | SORT AGGREGATE  |  |  1 |  2 |   |   | 
| 4 |  HASH GROUP BY  |  |  1 |  2 |   |   | 
|* 5 |  FILTER   |  |  |  |   |   | 
| 6 |  TABLE ACCESS FULL| DUAL |  1 |  2 |  2 (0)| 00:00:01 | 
| 7 | FAST DUAL   |  |  1 |  |  2 (0)| 00:00:01 | 
------------------------------------------------------------------------------- 

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

    5 - filter(NULL IS NOT NULL) 
+0

只有max *或* sum,將謂詞推入第一個分支不會獲得行;用'max(sum())'它得到一個空行的單行(你可以運行這個獨立的單元來檢查),這本身就是正確的(這正是尼古拉斯正在展示的)。但是正如你所說的將謂詞推入子查詢看起來在這種情況下是錯誤的。我猜這個優化器沒有考慮嵌套集合,當它決定這樣做,或者什麼... –

+0

@AlexPoole-我明白,從尼古拉斯的答案,他做出了一個完美的點(我沒有想到它);我與他之間唯一的分歧是他說這不是一個錯誤。 (我認爲,儘管他在閱讀後會同意我的評論。) – mathguy

+0

是的,只是在我開始時向自己解釋'奇怪'的部分 - 這確實有道理;在這種情況下,奇怪的過濾器在這兩種情況下都是有意義的。 –

3

不,這不是一個錯誤 。綜合功能是你看到這個意外結果的原因。下面是它的工作原理。 SUM()函數以及MAX()如果查詢沒有返回行,函數將返回NULL(產生1行)。當執行您的查詢優化器適用predicate pushing改造和原始查詢變爲(不會發布整個跟蹤,只有轉換後的查詢):

SELECT "from$_subquery$_001"."C1" "C1", 
     "from$_subquery$_001"."C2" "C2" 
    FROM ( 
      (SELECT 'ASDF' "C1",MAX(SUM(1)) "C2" 
       FROM "SYS"."DUAL" "DUAL" 
      WHERE 'ASDF'<>'ASDF'  [1]-- predicate pushed into the view 
      GROUP BY "DUAL"."DUMMY") 
      UNION 
      (SELECT 'JKLÖ' "C1", 
        1 "C2" 
       FROM "SYS"."DUAL" "DUAL" 
      WHERE 'JKLÖ'<>'ASDF')) "from$_subquery$_001" 

因爲謂語推你的拳頭子查詢的[1]不返回行和當一個集合函數(除了count和其他幾個),MAXSUM或甚至兩個,因爲在這種情況下使用空結果集NULL將返回-1行+ 1行由第二個子查詢返回,從而產生2行結果集你正在看。

下面是簡單的例子:

create table empty_table (c1 varchar2(1)); 

select 'aa' literal, nvl(max(c1), 'NULL') as res 
    from empty_table 

LITERAL RES 
------- ---- 
aa  NULL 

1 row selected. 
+2

你剛纔解釋說這**是一個錯誤。如果更改結果,則不應使用「UNION」或「UNION ALL」進行謂詞推送。該查詢在外部查詢中具有謂詞。推送它併產生不同的結果是優化器正在做的錯誤,所以這是一個錯誤。查詢本身既不要求預期謂詞被推送。 – mathguy

+2

@mathguy那麼這就是爲什麼我說這不是一個錯誤。我們有'UNION'集合運算符的內聯視圖,它可以防止視圖合併('UNION [ALL]防止視圖合併,而不是謂詞推送),優化器別無選擇,只能推動謂詞(發生過濾器下推轉換)。我們可以通過「實體化」(使用'materialize'提示)該視圖來防止謂詞推動。在深入瞭解這一點之後...是的,錯誤的結果迫使我們尋找工作。同意,看起來像錯誤。 –

+0

我們同意......在你說的話中,「優化器別無選擇,只能推謂詞」是我們不同意的部分,因爲優化器總是可以選擇將謂詞單獨留在原處。這是由Oracle控制的,推斷謂詞不是邏輯或標準所要求的。但正如我所說,現在很清楚,我們達成了一致。乾杯! – mathguy

0

一個非常簡單的例子導致同樣的錯誤:

SELECT 'ASDF' c1, MAX (SUM (1)) c2 
    FROM DUAL where 'ASDF' <> 'ASDF' 
GROUP BY dummy 

我必須承認,我完全糊塗了。爲什麼過濾器不消除記錄,從而消除了任何結果集?

Explain Plan from TOAD

+0

Nicholas Krasnov已經解釋了爲什麼從單獨查詢中得到一個返回null值的單行是正確的。問題在於過濾器應該稍後在OP的scneario中應用,其中聚合位於子查詢中。 –

相關問題