[英]Should SQL CTE's hold constant values?
我在今天的一個視圖中看到一段有趣的 SQL 代碼,其中 CTE 用於保存子查詢中使用的常量值。 雖然我能理解想要減少字符串值的重復出現背后的思考過程,但我以前從未見過這種做法,這讓我大吃一驚。 我更喜歡 .Net,所以我很想聽聽 SQL 的一些人關於這種模式是好的還是壞的做法的想法。
WITH myConstants AS (
SELECT 'myValue' AS myValue
)
SELECT
.......
(SELECT .......
WHERE x.myValue = mc.myValue
) AS mySubQuery
FROM
.......
INNER JOIN myConstants mc ON 1=1
您可以使用 CTE 以這種方式存儲常量,以便將范圍界定為一種“局部變量”(盡管我從未見過用於此目的的)。 使用包含常量的 CTE 完成語句后,您以后無法在同一批次中訪問它(除非您的語句將其存儲在其他地方)。 另一方面,您可以在同一批次中重復訪問一個變量。
例如
DECLARE @myVar VARCHAR(20) = 'test';
WITH myConstants AS (
SELECT 'myValue' AS myValue
)
SELECT myValue
FROM myConstants
-- can't access myConstants CTE or its content at this point
-- but can access the variable within the same batch...
SELECT @myVar AS MyVar
-- ...as many times as needed
SELECT @myVar AS MyVar
GO
我一直將 CTE 用於常數值。 是的,您可以像使用變量/參數一樣使用 CTE 值。 請注意此查詢:
WITH x(c1,c2,c3) AS (SELECT 1,2,3)
SELECT x.c2 AS xyz FROM x
UNION ALL
SELECT x.c1 FROM x
EXCEPT
SELECT x.c3 FROM x;
退貨:
xyz
----
2
1
細微差別是您每次執行時都必須包含對 CTE 的引用。 每次我“調用”一個“常量”時,我都必須包含FROM x
。
簡而言之,CTE 只是更簡潔的子查詢語法。 兩者之間的主要區別在於 CTE 可以是遞歸的,而子查詢不能。 另一方面,子查詢可以關聯。 請注意這三個查詢產生完全相同的結果和完全相同的執行計划:
DECLARE @sometable TABLE (SomeId INT);
INSERT @sometable VALUES(1),(1),(3),(4),(5);
-- CTE
WITH myConstants(c1,c2,c3) AS
(
SELECT 1, 2, 3
)
SELECT t.SomeId, Total = COUNT(*)
FROM @sometable AS t
CROSS JOIN myConstants AS m
WHERE t.SomeId IN (c1,c2,c3)
GROUP BY t.SomeId;
-- Subquery
SELECT t.SomeId, Total = COUNT(*)
FROM @sometable AS t
CROSS JOIN (SELECT 1,2,3) AS myConstants(c1,c2,c3)
WHERE t.SomeId IN (c1,c2,c3)
GROUP BY t.SomeId;
-- VALUES Constructor
SELECT t.SomeId, Total = COUNT(*)
FROM @sometable AS t
CROSS JOIN (VALUES(1,2,3)) AS myConstants(c1,c2,c3)
WHERE t.SomeId IN (c1,c2,c3)
GROUP BY t.SomeId;
CTE 與子查詢的好處是當您需要執行嵌套時。 考慮這個查詢(它使用上面相同的臨時表):
SELECT t.SomeId
FROM
(
SELECT t.SomeId
FROM @sometable AS t
WHERE t.SomeId < 10
) AS logic1
JOIN @sometable AS t
ON logic1.SomeId = t.SomeId
UNION ALL
SELECT TOP(2) logic3.SomeId
FROM
(
SELECT logic2.SomeId
FROM
(
SELECT t.SomeId
FROM
(
SELECT t.SomeId
FROM @sometable AS t
WHERE t.SomeId < 10
) AS logic1
JOIN @sometable AS t
ON logic1.SomeId = t.SomeId
) AS logic2
) AS logic3;
這可以像這樣簡化:
WITH
logic1 AS
(
SELECT t.SomeId
FROM @sometable AS t
WHERE t.SomeId < 10
),
logic2 AS
(
SELECT t.SomeId
FROM logic1 AS m
JOIN @sometable AS t
ON t.SomeId = m.SomeId
),
logic3 AS
(
SELECT TOP(2) m.SomeId
FROM logic2 AS m
)
SELECT m.SomeId
FROM logic2 AS m
UNION ALL
SELECT m.SomeId
FROM logic3 AS m;
同樣,兩者都返回完全相同的結果並生成完全相同的執行計划。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.