1

我對WITH語句(CTE)的理解是,它對每個查詢執行一次。有了這樣的查詢:WITH語句每個查詢執行一次還是每行執行一次?

WITH Query1 AS (...) 
SELECT * 
FROM 
    SomeTable t1 
    LEFT JOIN Query1 t2 ON ... 

如果這導致100行,我希望Query1是隻執行一次 - 不是100倍。如果該假設是正確的,則運行整個查詢所用的時間大致等於所花費的時間:運行Query1 +從SomeTable +加入SomeTableQuery1

我在的情況下:

單獨運行需要約5秒(400K行)時
  • Query1
  • 查詢的其餘部分在刪除WITH語句和LEFT JOIN後需要約15秒(400k行)。

因此,運行與WITH語句和整個查詢到位LEFT JOIN的時候,我本來期望的查詢及時完成,而不是我讓它運行了一個多小時,並一度停止它只有11k行。

我明顯錯了,但爲什麼?

回答

5

例子:

SET NOCOUNT ON; 
SET IMPLICIT_TRANSACTIONS ON; 

CREATE TABLE MyTable (MyID INT PRIMARY KEY); 
GO 
INSERT MyTable (MyID) 
VALUES (11), (22), (33), (44), (55); 

PRINT 'Test MyCTE:'; 
WITH MyCTE 
AS (
    SELECT *, ROW_NUMBER()OVER(ORDER BY MyID) AS RowNum 
    FROM MyTable 
) 
SELECT * 
FROM MyCTE crt 
LEFT JOIN MyCTE prev ON crt.RowNum=prev.RowNum+1; 

ROLLBACK; 

如果您在SSMS前面的腳本(按Ctrl+M - >實際執行計劃),那麼你會得到最後的查詢該執行計劃: enter image description here

在這種情況下, ,對於prev別名,對於crt別名和CTE執行一次,對於prev別名執行五次(!)次,對於來自crt的每一行,都執行一次。

所以,答案這個問題

確實與語句每行每查詢一次或執行一次?

both:每行每一次查詢(crt)和一次(prev:一次爲每個從crt)。

爲了優化該查詢,爲啓動, 1)可以嘗試從CTE(MyCTEQuery)的結果存儲到一個表中的變量或一個臨時表

2)定義的這個主鍵表作爲加入列表,

3)重寫最終查詢以使用此表變量或臨時表。

當然,您可以嘗試在CTE之間沒有這種自我聯接的情況下重寫最終查詢。

+0

感謝您的詳細回覆。這就說得通了。就我而言,我沒有兩次加入CTE,只有一次從普通桌面加入CTE。但我認爲你所說的仍然適用。對我來說,似乎很奇怪的是,SQL Server並沒有將CTE視爲一張臨時表 - 這就是我認爲它會起作用的原因。 –

+0

'CTE'是*本地* **視圖**。所以,何時使用,CTE會像其他視圖一樣展開。 –

+1

剛剛看到這篇文章時,試圖找出爲什麼我的查詢與12 CTE的慢。每個人執行超過8000次。謝謝你的提示! –