2013-01-31 83 views
1

我有這樣的結構:CTE執行多次

WITH my_cte 
    AS 
    (
    SELECT y.name 
    FROM 
     WHData.dbo.vw_data x 
     INNER JOIN WHData.dbo.vw_DimNames y 
       ON x.nameKey = y.CasinoKey 
    WHERE DateKey >= CONVERT(CHAR(8),DATEADD(mm,-2,GETDATE() - DAY(GETDATE())) + 1,112)--two complete months ago  
    GROUP BY y.name 
    ) 
SELECT * 
FROM WHAnalysis.dbo.tb_otherData a 
WHERE NOT EXISTS 
       (
      SELECT 1 
      FROM my_cte b 
      WHERE b.name = a.name 
      ); 

如果我在隔離運行在CTE代碼需要3秒;但完整的腳本只是運行並運行。

如果我離開CTE並使用索引臨時表,那麼它全部以4secs運行。

我假設發生的是CTE正在對tb_otherData中的每個數據記錄執行,因此可能需要,因爲有2000條記錄,2000 x 3秒....太長了!

臨時表解決方案很好,但出於興趣,有沒有辦法更改CTE代碼,以便它能夠快速運行?我缺少一些CTE技巧嗎?


編輯

如果我切換到一個良好的老式子查詢,則執行計劃是完全一致的:

SELECT * 
FROM WHAnalysis.dbo.tb_otherData a 
WHERE name not in 
     (
      SELECT y.name 
      FROM 
      WHData.dbo.vw_data x 
      INNER JOIN WHData.dbo.vw_DimNames y 
      ON x.nameKey = y.CasinoKey 
      WHERE DateKey >= CONVERT(CHAR(8),DATEADD(mm,-2,GETDATE() - DAY(GETDATE())) + 1,112)--two complete months ago 
       GROUP BY y.name 
      ); 
+0

熱膨脹係數是在這種情況下,只是語法糖 - 他們讓你覺得他們只評估一次,但往往並非如此。您是在尋找解釋還是解決方法(例如,在沒有臨時表的情況下編寫沒有CTE *和*的查詢)? –

+0

@AaronBertrand實際上,我剛剛測試過,執行計劃**完全相同,如果我切換到子查詢。我實際上想知道我是否可以保留CTE,但也許我錯過了一個讓它更快的技巧?附:和前一天稍微激烈的討論a_horse_with_no_name,他告訴我,我應該像'x ascolumn'這樣的列別名,但也提到了你的名字 - 我以爲你是學校的'mycolumn = x'? – whytheq

+1

是的,CTE只是寫一個子查詢的一種不同方式。如果我們不談論遞歸,他們可能會相同地進行優化。是的,我是'別名=專欄',但它只是一個偏好,不值得爭論。我會反對使用'column'作爲'alias''或'alias'= column'的人,但僅僅是爲了擺脫困惑和棄用的愚蠢的字符串分隔符。 –

回答

2

嘗試使用,而不是使用子查詢與此查詢EXISTS:

SELECT a. * 
FROM WHAnalysis.dbo.tb_otherData a 
LEFT JOIN 
( SELECT y.name as name 
    FROM WHData.dbo.vw_data x 
     INNER JOIN WHData.dbo.vw_DimNames y 
       ON x.nameKey = y.CasinoKey 
     WHERE DateKey >= 
      CONVERT(CHAR(8),DATEADD(mm,-2,GETDATE() - DAY(GETDATE())) + 1,112)  
) b on a.name=b.name 
WHERE b.name is null 

If DateKeyWHData.dbo.vw_data那麼這將是:

SELECT a. * 
FROM WHAnalysis.dbo.tb_otherData a 
LEFT JOIN WHData.dbo.vw_DimNames y on a.name=y.name 
LEFT JOIN WHData.dbo.vw_data x 
    on y.CasinoKey= x.nameKey 
     and (
      x.DateKey >= 
      CONVERT(CHAR(8),DATEADD(mm,-2,GETDATE() - DAY(GETDATE())) + 1,112) 
     ) 
WHERE x.nameKey is null 
+0

+1 this L.O.J是現貨;由於某些原因,它要快得多 - 使用LOJ時的執行計劃沒有嵌套循環;也許這就是收益。 – whytheq

+0

如果有可能,應該避免使用JOIN來嵌套子查詢和IN子查詢。 – valex

+0

@valex爲什麼?在很多情況下,EXISTS/NOT EXISTS比JOIN執行得更好(對於內連接和不可空列,IN/EXISTS可以產生完全相同的計劃)。 [這是我寫在NOT EXISTS上的博客文章](http://sqlperformance.com/left-anti-semi-join)。但是你應該總是測試一下,而不是一概而論。 –