2016-07-28 27 views
1

我一直認爲With子句是作爲一次性執行語句工作的,它的行爲與普通表一樣 - 您可以像在常規表上一樣對其執行所有SQL操作。使用子句執行

但事實證明,在多個數據庫(Oracle,Netezza,Sybase,Teradata)中,每次使用with子句時都會執行該子句。

With Test as(

    select random() --pseudo code 

) 
select '1st select', * from Test 
union 
select '2nd select', * form Test 

而不是2個相同的數字,上面的查詢返回2個不同的數字,所以它是爲每個選擇執行。

如果我在With子句中有一個非常複雜的查詢,並且在查詢的其餘部分使用了5次,它將執行5次,這對我來說似乎非常無效。

那麼有人能給我一個很好的邏輯理由嗎?

+0

Postgres的返回兩個外選擇相同的號碼。所以CTE只運行一次 –

+0

在Oracle'WITH test(rnd)AS(SELECT/* + MATERIALIZE */DBMS_RANDOM.VALUE FROM DUAL)SELECT'a',t。* FROM test t UNION ALL SELECT'b', t。* FROM test t;'給出了兩個相同的數字。 – MT0

回答

0

對於Oracle:

CTE又名子查詢分解條款(Oracle的術語)可以被內聯或物化,這曾經是可行的。 CBO應該決定什麼會更有效。你的例子與ramdom()函數是一個角落的情況。

嘗試使用提示MATERIALIZE或INLINE來查看exec如何。計劃變更。您的測試沒有表達任何關於真正SQL查詢評估的行爲。

1

oracle world中,如解釋here所述,WITH query_name子句可讓您爲子查詢塊指定一個名稱。然後,您可以通過指定查詢名稱來引用查詢中的多個位置的子查詢塊。 Oracle通過將查詢名稱視爲內聯視圖或臨時表來優化查詢。

您可以在任何頂級SELECT語句和大多數類型的子查詢中指定此子句。查詢名稱對主查詢以及除定義查詢名稱本身的子查詢以外的所有後續子查詢都可見。

如果WITH查詢的結果在主查詢的正文中需要多次,例如需要將一個平均值與兩次或三次進行比較,那麼WITH子句最有價值。關鍵是要將多次加入表的訪問次數最小化爲單個查詢。在子查詢分解

限制:

不能嵌套這一條款。也就是說,您不能在另一個subquery_factoring_clause的子查詢中指定subquery_factoring_clause。但是,在一個subquery_factoring_clause中定義的query_name可以用於任何後續subquery_factoring_clause的子查詢中。

在使用集合運算符的查詢中,set運算符子查詢不能包含subquery_factoring_clause,但FROM子查詢可以包含subquery_factoring_clause。

在你的情況中,你已經使用了一個隨機函數,它將被優化器視爲不同的方式,它將視爲內聯視圖而不是物化視圖。由於@ ibre5041建議在不同情況下使用EXPLAIN PLAN

考慮一個遞歸CTE的情況,它每次都在內部使用。

WITH generator (value) AS (
    SELECT 1 FROM DUAL 
UNION ALL 
    SELECT value + 1 
    FROM generator 
    WHERE value < 10 
) 
SELECT value 
FROM generator; 

Plan hash value: 1492144221 

-------------------------------------------------------------------------------------------------- 
| Id | Operation         | Name | Rows | Bytes | Cost (%CPU)| Time  | 
-------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT       |  |  2 | 26 |  4 (0)| 00:00:01 | 
| 1 | VIEW          |  |  2 | 26 |  4 (0)| 00:00:01 | 
| 2 | UNION ALL (RECURSIVE WITH) BREADTH FIRST|  |  |  |   |   | 
| 3 | FAST DUAL        |  |  1 |  |  2 (0)| 00:00:01 | 
|* 4 | RECURSIVE WITH PUMP     |  |  |  |   |   | 
-------------------------------------------------------------------------------------------------- 

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

    4 - filter("VALUE"<10) 
1

至少在Teradata它的工作如預期的,隨機值只計算一次:

With Test as(
    select random(1,1000000) as x --pseudo code 
) 
select '1st select', x from Test 
union 
select '2nd select', x from Test 
; 

*** Query completed. 2 rows found. 2 columns returned. 
*** Total elapsed time was 1 second. 

'1st select'   x 
------------ ----------- 
1st select   422654 
2nd select   422654 
+0

我不會說「如預期的那樣」 - 對不同人的期望是不同的。我只與Oracle合作過。我閱讀了子查詢保理的文檔,在這種情況下我期望的是得到不同的值,因爲這是CTE在這種情況下(在Oracle中)所述的,記錄在案的行爲。 – mathguy

+0

@mathguy:這就是爲什麼有標準和基於標準SQL *公用表表達式*必須在多次訪問時返回相同的數據。 – dnoeth

+0

如果供應商選擇忽視標準(因爲他們是免費的),並且他們記錄了這些標準,那麼我仍然「期望」某項功能的行爲與供應商所記錄的相同,而不是標準所述。在Oracle中,我期望'null || 'x''返回''x',當我完全熟悉標準需要'null'時。我同意這個標準,並且我發現Oracle的選擇很奇怪(有時甚至是愚蠢的),但我仍然認爲當我使用他們的產品時,這種行爲就是甲骨文所說的。 – mathguy

相關問題