簡體   English   中英

SQL CTE 的值應該保持不變嗎?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM