SQL Server 公用表表達式(CTE)實現遞歸

来源:http://www.cnblogs.com/Brambling/archive/2017/04/28/6778814.html
-Advertisement-
Play Games

公用表表達式簡介: 公用表表達式 (CTE) 可以認為是在單個 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句的執行範圍內定義的臨時結果集。CTE 與派生表類似,具體表現在不存儲為對象,並且只在查詢期間有效。與派生表的不同之處在於,公用表表達式 (CTE) ...


公用表表達式簡介:

公用表表達式 (CTE) 可以認為是在單個 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 語句的執行範圍內定義的臨時結果集。CTE 與派生表類似,具體表現在不存儲為對象,並且只在查詢期間有效。與派生表的不同之處在於,公用表表達式 (CTE) 具有一個重要的優點,那就是能夠引用其自身,從而創建遞歸 CTE。遞歸 CTE 是一個重覆執行初始 CTE 以返回數據子集直到獲取完整結果集的公用表表達式。

 

下麵先創建一個表,並插入一些數據:

create table Role_CTE
(
    Id        int             not null,
    Name    nvarchar(32) not null,
    ParentId    int        not null 
)

insert into Role_CTE(Id,Name,ParentId)
select '1','超級管理員','0' union 
select '2','管理員A','1' union 
select '3','管理員B','2' union 
select '4','會員AA','2' union 
select '5','會員AB','2' union 
select '6','會員BA','3' union 
select '7','會員BB','3' union 
select '8','用戶AAA','4' union 
select '9','用戶BBA','7'  

-- 創建一個複合聚集索引
create clustered index Clu_Role_CTE_Index
on Role_CTE(Id,ParentId)
with
(
    pad_index=on,
    fillfactor=50,
    drop_existing=off,
    statistics_norecompute=on
)

select * from Role_CTE

查找指定節點的所有子孫節點:

使用普通 sql 語句實現:

declare @level  int
declare @node    int

declare @ResTab table
(
    node    int not null,
    lv        int not null    
)

set @level=0        -- 表示初始的等級
set @node=3            --表示初始的節點ID,即從指定的哪個節點開始查找

insert into @ResTab                -- 為表變數插入初始的數據
select Id,@level 
from Role_CTE 
where Id=@node

while(@@ROWCOUNT>0)
begin
    set @level=@level+1

    insert into @ResTab
    select b.Id,@level 
    from @ResTab a 
    join Role_CTE b on a.node=b.ParentId and lv=@level-1    -- join 等於 inner join(內連接)和自連接
end

select a.node,b.Name,a.lv 
from @ResTab a 
left join Role_CTE b on a.node=b.Id

以上是根據指定節點ID(3),查找父節點ID(即欄位 ParentId)等於指定的節點ID,如果有就插入,並繼續迴圈。

PS:lv=@level-1 是重點,不然會進入死迴圈,作用就是限制只插入一次。

如果需要限制迴圈的次數,即遞歸的層數,那麼只需要在 while 條件裡面添加一個限制即可。如下:

declare @level  int
declare @node    int
declare @num    int

declare @ResTab table
(
    node    int not null,
    lv        int not null    
)

set @level=0        -- 表示初始的等級
set @node=3            --表示初始的節點ID,即從指定的哪個節點開始查找
set @num=1        -- 指定遞歸層級,即迴圈的次數

insert into @ResTab                -- 為表變數插入初始的數據
select Id,@level 
from Role_CTE 
where Id=@node

while(@@ROWCOUNT>0 and @level<@num)
begin
    set @level=@level+1

    insert into @ResTab
    select b.Id,@level 
    from @ResTab a 
    join Role_CTE b on a.node=b.ParentId and lv=@level-1    -- join 等於 inner join(內連接)和自連接
end

select a.node,b.Name,a.lv 
from @ResTab a 
left join Role_CTE b on a.node=b.Id

當然,如果指定了迴圈次數,就可以不用 while 判斷語句的 @@rowcount>0 了。

 

使用 SQL CTE 實現:

declare @node    int 

set @node=3;

with temp_cte
as
(
    select Id,Name,0 lv        -- 查詢出“根節點”,即指定的起始節點
    from Role_CTE 
    where Id=@node 

    union all

    select b.Id,b.Name,a.lv+1 
    from temp_cte a 
    join Role_CTE b on a.Id=b.ParentId
)

select * from temp_cte

使用 CTE 控制遞歸的層數,與上面類似。如下:

declare @node    int 
declare @num    int

set @node=3;
set @num=1;

with temp_cte
as
(
    select Id,Name,0 lv        -- 查詢出“根節點”,即指定的起始節點
    from Role_CTE 
    where Id=@node 

    union all

    select b.Id,b.Name,a.lv+1 
    from temp_cte a 
    join Role_CTE b on a.Id=b.ParentId
                    and a.lv<@num        --控制遞歸層數
)

select * from temp_cte

 

查找指定節點的所有祖先節點:

使用普通 sql 語句實現:

declare @level  int
declare @node    int
declare @num    int

declare @ResTab table
(
    node    int not null,
    lv        int not null    
)

set @level=0    -- 表示初始的等級
set @node=8            --表示初始的節點ID,即從指定的哪個節點開始查找
set @num=2        -- 指定遞歸層級,即迴圈的次數

while(@level<=@num and @node is not null)    -- 如果為空就表示沒有查到父級了
begin
    
    insert into @ResTab
    select @node,@level

    set @level=@level+1

    select @node=ParentId 
    from Role_CTE 
    where Id=@node

end

select a.node,b.Name,a.lv 
from @ResTab a 
left join Role_CTE b on a.node=b.Id

使用 SQL CTE 實現:

declare @node    int 
declare @num    int

set @node=8;
set @num=2;

with temp_cte
as
(
    select Id,Name,ParentId,0 lv        -- 查詢出“根節點”,即指定的起始節點
    from Role_CTE 
    where Id=@node 

    union all

    select b.Id,b.Name,b.ParentId,a.lv+1 
    from temp_cte a 
    join Role_CTE b on a.ParentId=b.Id
                    and a.lv < @num        --控制遞歸層數
)

select * from temp_cte

 

參考:

https://msdn.microsoft.com/zh-cn/library/ms186243.aspx

https://msdn.microsoft.com/zh-cn/library/ms175972.aspx

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 收錄待用,修改轉載已取得 "騰訊雲" 授權 前言 前面總結的幾篇spark踩坑博文中,我總結了自己在使用spark過程當中踩過的一些坑和經驗。我們知道Spark是多機器集群部署的,分為Driver/Master/Worker,Master負責資源調度,Worker是不同的運算節點,由Master統一 ...
  • BULK COLLECT(成批聚合類型)和數組集合type類型is table of 表%rowtype index by binary_integer用法筆記 例1: 批量查詢項目資金賬戶號為 "320001054663"的房屋賬戶信息並把它們列印出來 . 說明部分: 1. DECLARE 說明以 ...
  • 前言: 由於很多業務表因為歷史原因或者性能原因,都使用了違反第一範式的設計模式。即同一個列中存儲了多個屬性值(具體結構見下表)。 這種模式下,應用常常需要將這個列依據分隔符進行分割,並得到列轉行的結果。 表數據: 期望得到結果: 總結: 這種方法的缺點在於,我們需要一個擁有連續數列的獨立表(這裡是i ...
  • /** *@author:zhengwei *@date:2017-04-28 *@desc:存儲過程用法總結 */ CREATE OR REPLACE PROCEDURE MYPROCEDURE(P_ID IN VARCHAR, P_STATUS OUT VARCHAR) --P... ...
  • MongoVUE 不能正常顯示 Collections 問題的解決 ...
  • 1. 刪除所有MySQL中的用戶(包括root用戶): root可以保留,然後修改為其他用戶 2. 為管理員root用戶設置密碼 : A、為沒有密碼的用戶設置密碼: B、MySQL多實例設置密碼需要指定mysql.sock文件: C、新版本的mysql可能在第一次啟動以後會生成一個隨機密碼: D、直 ...
  • Data privacy is a major concern today for any organization that manages sensitive data or personally identifiable information (PII). Examples of such ...
  • 之前就想要把一些 SQL 的常用函數記錄下來,不過一直沒有實行。。。嘿嘿。。。 直到今天用到substring()這個函數,C# 裡面這個方法起始值是 0,而 SQL 裡面起始值是 1。傻傻分不清楚。。。 這篇博客作為記錄 SQL 的函數的使用方法,想到哪裡用到哪裡就寫到哪裡。。。 SubStrin ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...