首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> SQL Server 2005

SQL Server 2005 CTE 共用資料表運算式完整教學:遞迴查詢與多次引用實戰

     編輯:SQL Server 2005

SQL Server 2005 引入 CTE (Common Table Expression,共用資料表運算式),提供宣告式、可重複引用、支援遞迴的暫時結果集,取代暫存表/表變數的許多用途。本文系統介紹 CTE 語法結構、優點、適用範圍,並以「遞迴階層查詢」為核心範例,完整對比 SQL 2000 游標/暫存表寫法與 CTE 遞迴寫法,展現 CTE 的簡潔威力。

CTE 基本語法

WITH cte_name [(column_list)] AS (
    -- 定義查詢 (錨點成員)
    SELECT ...
)
-- 主查詢:可多次引用 CTE
SELECT * FROM cte_name;

CTE 特性優勢

特性說明對比暫存表/表變數
不持久化僅在單一陳述式執行期間存在暫存表需手動 DROP,表變數隨批次結束
可自我引用 (遞迴)UNION ALL 結合錨點與遞迴成員暫存表/表變數無法直接遞迴
同一查詢多次引用主查詢、子查詢、JOIN 皆可重用暫存表需重複 INSERT 或 JOIN 多次
可讀性邏輯由上而下,宣告式暫存表程序式、分散在多個陳述式
範圍單一 SELECT/INSERT/UPDATE/DELETE/CREATE VIEW暫存表跨批次/程序可見

簡單 CTE 範例:取得每組最大薪資

WITH MaxSalaryCTE (dept_id, max_sal) AS (
    SELECT dept_id, MAX(salary) FROM Employee GROUP BY dept_id
)
SELECT e.* 
FROM Employee e JOIN MaxSalaryCTE m ON e.dept_id = m.dept_id AND e.salary = m.max_sal;

核心範例:遞迴階層查詢 (組織架構/分類樹/帳款科目)

資料結構

CREATE TABLE Co_ItemNameSet (
    ItemId INT PRIMARY KEY,
    ParentItemId INT,
    ItemName NVARCHAR(100)
);

-- 範例資料
INSERT INTO Co_ItemNameSet VALUES
(2, 0, N'管理費用'),
(3, 0, N'銷售費用'),
(5, 0, N'生產成本'),
(35, 5, N'材料'),
(36, 5, N'人工'),
(37, 5, N'製造費用'),
(38, 35, N'原材料'),
(39, 35, N'主要材料'),
(40, 35, N'輔助材料'),
(41, 36, N'工資'),
(42, 36, N'福利'),
(43, 36, N'年終獎金'),
(52, 2, N'汽車費用'),
(53, 52, N'燃料'),
(54, 52, N'輪胎'),
(55, 2, N'招聘費'),
(56, 2, N'排污費');

需求:查詢 ItemId=2 (管理費用) 及其所有下層節點

SQL 2000 寫法:游標 + 暫存表模擬堆疊 (複雜、易錯、效能差)

DECLARE @i INT = 2;
CREATE TABLE #stack (ItemId INT, lvl INT);
CREATE TABLE #result (ItemId INT, ParentItemId INT, ItemName NVARCHAR(100), lvl INT);

INSERT #stack SELECT ItemId, 1 FROM Co_ItemNameSet WHERE ItemId = @i;
INSERT #result SELECT ItemId, ParentItemId, ItemName, 1 FROM Co_ItemNameSet WHERE ItemId = @i;

DECLARE @lvl INT = 1, @current INT = 0;
WHILE @lvl > 0
BEGIN
    SELECT @current = ItemId FROM #stack WHERE lvl = @lvl;
    IF @@ROWCOUNT > 0
    BEGIN
        DELETE #stack WHERE lvl = @lvl AND ItemId = @current;
        INSERT #stack SELECT ItemId, @lvl + 1 FROM Co_ItemNameSet WHERE ParentItemId = @current;
        INSERT #result SELECT ItemId, ParentItemId, ItemName, @lvl + 1 FROM Co_ItemNameSet WHERE ParentItemId = @current;
        IF @@ROWCOUNT > 0 SET @lvl += 1;
    END
    ELSE SET @lvl -= 1;
END

SELECT * FROM #result;
DROP TABLE #stack, #result;

SQL 2005+ CTE 遞迴寫法:簡潔、宣告式、易維護

DECLARE @i INT = 2;

WITH Co_ItemNameSet_CTE (ItemId, ParentItemId, ItemName, Lvl) AS (
    -- 錨點成員:起始節點
    SELECT ItemId, ParentItemId, ItemName, 1
    FROM Co_ItemNameSet
    WHERE ItemId = @i
    
    UNION ALL
    
    -- 遞迴成員:連接父子關係
    SELECT c.ItemId, c.ParentItemId, c.ItemName, cte.Lvl + 1
    FROM Co_ItemNameSet c
    INNER JOIN Co_ItemNameSet_CTE cte ON c.ParentItemId = cte.ItemId
)
SELECT * FROM Co_ItemNameSet_CTE;

結果 (兩者相同)

ItemId ParentItemId ItemName     Lvl
2      0            管理費用      1
52     2            汽車費用      2
55     2            招聘費        2
56     2            排污費        2
53     52           燃料          3
54     52           輪胎          3

CTE 遞迴核心規則

  1. 兩部分組成:錨點成員 (UNION ALL 上方) + 遞迴成員 (下方)
  2. 遞迴成員必須引用 CTE 自身 (如 JOIN Co_ItemNameSet_CTE)
  3. 預設最大遞迴 100 層,超過需 OPTION (MAXRECURSION 0) (0=無限制) 或具體數字
  4. 遞迴終止條件:遞迴成員回傳空集時自動停止
  5. 欄位數、順序、型別:錨點與遞迴成員必須完全一致

進階遞迴應用

1. 產生連續日期序列

WITH Dates AS (
    SELECT CAST('2024-01-01' AS DATE) AS dt
    UNION ALL
    SELECT DATEADD(DAY, 1, dt) FROM Dates WHERE dt < '2024-12-31'
)
SELECT * FROM Dates OPTION (MAXRECURSION 0);

2. 字串拆解 (類似 fn_MyData)

WITH Split AS (
    SELECT 1 AS id, 
           LEFT(@str, CHARINDEX(',', @str+',')-1) AS val,
           STUFF(@str, 1, CHARINDEX(',', @str+','), '') AS rest
    UNION ALL
    SELECT id+1, LEFT(rest, CHARINDEX(',', rest+',')-1), STUFF(rest, 1, CHARINDEX(',', rest+','), '')
    FROM Split WHERE rest > ''
)
SELECT id, val FROM Split;

3. 組織圖路徑顯示 (路徑串接)

WITH OrgPath AS (
    SELECT EmployeeID, ManagerID, Name, CAST(Name AS NVARCHAR(MAX)) AS Path, 1 AS Lvl
    FROM Employee WHERE ManagerID IS NULL
    UNION ALL
    SELECT e.EmployeeID, e.ManagerID, e.Name, CAST(o.Path + ' > ' + e.Name AS NVARCHAR(MAX)), o.Lvl + 1
    FROM Employee e JOIN OrgPath o ON e.ManagerID = o.EmployeeID
)
SELECT * FROM OrgPath;

效能與限制

  • 遞迴 CTE 逐層執行,大深度/寬度階層可能較慢,考慮 HierarchyID (SQL 2008+) 或物化路徑
  • MAXRECURSION 0 謹慎使用,防止無限遞迴耗盡資源
  • CTE 非物化,每次引用重新計算;複雜 CTE 多次引用考慮改用暫存表物化
  • CTE 可用於 INSERT、UPDATE、DELETE、MERGE、CREATE VIEW

適用版本

  • SQL Server 2005+ (基本 CTE、遞迴)
  • SQL Server 2008+ (HierarchyID 型別優化階層)
  • Azure SQL Database / Managed Instance / SQL Server on Linux
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved