【Sql遞歸查詢】Mysql、Oracle、SQL Server、PostgreSQL 實現遞歸查詢的區別與案例(詳解)

在這里插入圖片描述

文章目錄

    • Mysql 5.7 遞歸查詢
    • Mysql 8 實現遞歸查詢
    • Oracle遞歸示例
    • SQL Server 遞歸查詢示例
    • PostgreSQL 遞歸查詢示例

更多相關內容可查看

Mysql 5.7 遞歸查詢

MySQL 5.7 本身不直接支持標準 SQL 中的遞歸查詢語法(如 WITH RECURSIVE 這種常見的遞歸查詢方式),但可以通過使用存儲過程、臨時表或自連接等方式來實現遞歸查詢的效果。

  1. 使用自連接實現遞歸查詢

通過自連接的方式模擬遞歸查詢,適合處理簡單的遞歸結構。假設我們有一個表示部門層級關系的表 departments,結構如下:

CREATE TABLE departments (id INT PRIMARY KEY,name VARCHAR(50),parent_id INT
);

向表中插入一些示例數據:

INSERT INTO departments (id, name, parent_id) VALUES
(1, '總公司', NULL),
(2, '研發部', 1),
(3, '市場部', 1),
(4, '研發一組', 2),
(5, '研發二組', 2);

使用自連接查詢所有部門及其子部門:

在這里插入圖片描述

SELECTt1.id AS root_id,t1.name AS root_name,t2.id AS child_id,t2.name AS child_name
FROMdepartments t1
JOINdepartments t2
ONt1.id = t2.parent_id
UNION
SELECTid AS root_id,name AS root_name,id AS child_id,name AS child_name
FROMdepartments
WHEREparent_id IS NULL;

在這個查詢中,通過 JOIN 語句將父部門和子部門關聯起來,然后使用 UNION 操作符將頂級部門(parent_idNULL)也包含在結果中。

  1. 使用存儲過程實現遞歸查詢
DELIMITER //-- 創建一個名為 recursive_departments_func 的函數,該函數接收兩個整數參數 p_parent_id 和 p_level,并返回一個整數
CREATE FUNCTION recursive_departments_func(p_parent_id INT, p_level INT) RETURNS INT
DETERMINISTIC
BEGIN-- 聲明一個整數變量 done,用于標記游標是否已經完成遍歷,初始值為 FALSEDECLARE done INT DEFAULT FALSE;-- 聲明一個整數變量 v_id,用于存儲從游標中獲取的部門 idDECLARE v_id INT;-- 聲明一個字符串變量 v_name,用于存儲從游標中獲取的部門名稱DECLARE v_name VARCHAR(50);-- 聲明一個游標 cur,用于查詢 departments 表中 parent_id 等于 p_parent_id 的記錄DECLARE cur CURSOR FORSELECT id, name FROM departments WHERE parent_id = p_parent_id;-- 聲明一個繼續處理程序,當游標沒有更多數據時,將 done 置為 TRUEDECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;-- 創建一個臨時表 temp_departments,用于存儲遞歸調用的結果-- 僅在該表不存在時創建,包含三個列:id、name 和 levelCREATE TEMPORARY TABLE IF NOT EXISTS temp_departments (id INT,name VARCHAR(50),level INT);-- 打開游標 cur,以便開始讀取數據OPEN cur;-- 定義一個名為 read_loop 的循環標簽read_loop: LOOP-- 從游標 cur 中獲取數據并存儲到 v_id 和 v_name 中FETCH cur INTO v_id, v_name;-- 檢查 done 變量是否為 TRUE,如果是則離開循環IF done THENLEAVE read_loop;END IF;-- 將當前部門的信息插入到臨時表 temp_departments 中INSERT INTO temp_departments (id, name, level) VALUES (v_id, v_name, p_level);-- 遞歸調用 recursive_departments_func 函數,將當前部門的 id 作為新的父部門 id,層級加 1SET @result = recursive_departments_func(v_id, p_level + 1);END LOOP;-- 關閉游標 curCLOSE cur;-- 函數最終返回 1RETURN 1;
END //DELIMITER ;

Mysql 8 實現遞歸查詢

在 MySQL 8 中,可以使用 WITH RECURSIVE 子句來實現遞歸查詢。

  1. 創建示例數據

假設我們有一個表示員工層級關系的表 employees,其中包含員工編號、姓名、上級員工編號:

-- 創建表
CREATE TABLE employees (employee_id INT PRIMARY KEY,name VARCHAR(50),manager_id INT,FOREIGN KEY (manager_id) REFERENCES employees(employee_id)
);-- 插入數據
INSERT INTO employees (employee_id, name, manager_id) VALUES
(1, 'CEO', NULL),
(2, 'CTO', 1),
(3, 'CFO', 1),
(4, 'Lead Developer', 2),
(5, 'Developer 1', 4),
(6, 'Developer 2', 4),
(7, 'Accountant', 3);
  1. 遞歸查詢所有員工及其下屬

使用 WITH RECURSIVE 子句進行遞歸查詢,查找某個員工及其所有下屬。以下是查詢 CEO 及其所有下屬的示例:

WITH RECURSIVE employee_hierarchy AS (-- 初始查詢,找到CEOSELECT employee_id, name, manager_idFROM employeesWHERE name = 'CEO'UNION ALL-- 遞歸部分,找到下屬員工SELECT e.employee_id, e.name, e.manager_idFROM employees eINNER JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;
  • 2.1. CTE(公共表表達式)定義
    • WITH RECURSIVE employee_hierarchy AS (...) 定義了一個名為 employee_hierarchy 的遞歸 CTE。
    • 初始查詢部分:
      SELECT employee_id, name, manager_id
      FROM employees
      WHERE name = 'CEO'
      
      這部分找到 CEO 的記錄,作為遞歸的起點。
    • UNION ALL 用于將初始查詢結果和遞歸查詢結果合并。
    • 遞歸部分:
      SELECT e.employee_id, e.name, e.manager_id
      FROM employees e
      INNER JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
      
      這部分通過連接 employees 表和遞歸生成的 employee_hierarchy 表,找到每個員工的下屬。
  1. 反向遞歸查詢(查找某個員工的所有上級)

查找某個員工(例如 Developer 1)的所有上級:

WITH RECURSIVE manager_hierarchy AS (-- 初始查詢,找到Developer 1SELECT employee_id, name, manager_idFROM employeesWHERE name = 'Developer 1'UNION ALL-- 遞歸部分,找到上級員工SELECT e.employee_id, e.name, e.manager_idFROM employees eINNER JOIN manager_hierarchy mh ON e.employee_id = mh.manager_id
)
SELECT * FROM manager_hierarchy;

這個查詢同樣使用 WITH RECURSIVE,但遞歸方向是從指定員工向上查找其所有上級。

  1. 組織遞歸查詢示例
-- 假設我們有一個 organizations 表存儲組織信息
CREATE TABLE organizations (id INT PRIMARY KEY AUTO_INCREMENT,parent_id INT,name VARCHAR(255),level INT
);-- 假設我們有一個 employees 表存儲員工信息
CREATE TABLE employees (id INT PRIMARY KEY AUTO_INCREMENT,name VARCHAR(255),organization_id INT
);-- 插入一些示例數據到 organizations 表
INSERT INTO organizations (parent_id, name, level) VALUES(NULL, '總公司', 1),(1, '分公司 A', 2),(1, '分公司 B', 2),(2, '部門 A1', 3),(2, '部門 A2', 3),(3, '部門 B1', 3),(3, '部門 B2', 3),(4, '小組 A1-1', 4),(4, '小組 A1-2', 4);-- 插入一些示例數據到 employees 表
INSERT INTO employees (name, organization_id) VALUES('員工 1', 1),('員工 2', 2),('員工 3', 2),('員工 4', 3),('員工 5', 4),('員工 6', 4),('員工 7', 4),('員工 8', 5),('員工 9', 6),('員工 10', 7),('員工 11', 8);-- 使用 WITH RECURSIVE 進行遞歸查詢
WITH RECURSIVE organization_hierarchy AS (-- 非遞歸部分:選擇根組織作為起始點SELECT id, parent_id, name, level, 0 AS depthFROM organizationsWHERE id = 1UNION ALL-- 遞歸部分:選擇子組織,深度加 1SELECT o.id, o.parent_id, o.name, o.level, oh.depth + 1FROM organizations oJOIN organization_hierarchy oh ON o.parent_id = oh.id
)
-- 從遞歸結果中選擇信息并統計員工數量
SELECT oh.id, oh.parent_id, oh.name, oh.level, oh.depth, COUNT(e.id) AS employee_count
FROM organization_hierarchy oh
LEFT JOIN employees e ON oh.id = e.organization_id
GROUP BY oh.id, oh.parent_id, oh.name, oh.level, oh.depth
ORDER BY oh.depth, oh.id;

Oracle遞歸示例

  • 支持版本:Oracle 9i 開始引入遞歸查詢的功能,通過 CONNECT BY 子句實現。從 Oracle 11g 開始支持使用 WITH RECURSIVE 語法(CTE 遞歸查詢)。
  • 示例1:假設有一個表示部門層級關系的表 departments,結構為 (department_id, department_name, parent_department_id)
-- 使用 CONNECT BY 子句
SELECT department_id, department_name, parent_department_id
FROM departments
START WITH parent_department_id IS NULL
CONNECT BY PRIOR department_id = parent_department_id;-- 使用 WITH RECURSIVE 語法
WITH RECURSIVE department_hierarchy AS (SELECT department_id, department_name, parent_department_idFROM departmentsWHERE parent_department_id IS NULLUNION ALLSELECT d.department_id, d.department_name, d.parent_department_idFROM departments dINNER JOIN department_hierarchy dh ON d.parent_department_id = dh.department_id
)
SELECT * FROM department_hierarchy;
  • 示例2:使用 CONNECT BY 和 START WITH 子句進行遞歸查詢,以查詢 id 為 1 的組織(總公司)及其所有子組織。
CREATE TABLE organizations (id        NUMBER PRIMARY KEY,parent_id NUMBER,name      VARCHAR2(100)
);INSERT INTO organizations (id, parent_id, name) VALUES (1, NULL, '總公司');
INSERT INTO organizations (id, parent_id, name) VALUES (2, 1, '分公司 A');
INSERT INTO organizations (id, parent_id, name) VALUES (3, 1, '分公司 B');
INSERT INTO organizations (id, parent_id, name) VALUES (4, 2, '部門 A1');
INSERT INTO organizations (id, parent_id, name) VALUES (5, 2, '部門 A2');
INSERT INTO organizations (id, parent_id, name) VALUES (6, 3, '部門 B1');
INSERT INTO organizations (id, parent_id, name) VALUES (7, 3, '部門 B2');
INSERT INTO organizations (id, parent_id, name) VALUES (8, 4, '小組 A1-1');
INSERT INTO organizations (id, parent_id, name) VALUES (9, 4, '小組 A1-2');SELECT o.id, o.parent_id, o.name, LEVEL
FROM organizations o
START WITH o.id = 1
CONNECT BY PRIOR o.id = o.parent_id;
  • 示例3:使用遞歸查詢和 JOIN 操作計算每個組織及其子組織的員工總數。
CREATE TABLE employees (id           NUMBER PRIMARY KEY,name         VARCHAR2(100),organization_id NUMBER
);INSERT INTO employees (id, name, organization_id) VALUES (1, '員工 1', 2);
INSERT INTO employees (id, name, organization_id) VALUES (2, '員工 2', 2);
INSERT INTO employees (id, name, organization_id) VALUES (3, '員工 3', 3);
INSERT INTO employees (id, name, organization_id) VALUES (4, '員工 4', 4);
INSERT INTO employees (id, name, organization_id) VALUES (5, '員工 5', 4);
INSERT INTO employees (id, name, organization_id) VALUES (6, '員工 6', 5);
INSERT INTO employees (id, name, organization_id) VALUES (7, '員工 7', 6);
INSERT INTO employees (id, name, organization_id) VALUES (8, '員工 8', 7);
INSERT INTO employees (id, name, organization_id) VALUES (9, '員工 9', 8);WITH org_hierarchy AS (SELECT o.id, o.parent_id, o.name, LEVEL AS org_levelFROM organizations oSTART WITH o.id = 1CONNECT BY PRIOR o.id = o.parent_id
)
SELECT oh.id, oh.parent_id, oh.name, oh.org_level, COUNT(e.id) AS employee_count
FROM org_hierarchy oh
LEFT JOIN employees e ON oh.id = e.organization_id
GROUP BY oh.id, oh.parent_id, oh.name, oh.org_level
ORDER BY oh.org_level, oh.id;
  • 示例4:假設 organizations 表有一個 budget 列表示組織的預算,并且預算可以從父組織分配給子組織。我們可以使用遞歸查詢計算每個組織及其子組織的最終預算
ALTER TABLE organizations ADD (budget NUMBER);UPDATE organizations SET budget = 100000 WHERE id = 1;
UPDATE organizations SET budget = 0 WHERE id IN (2, 3);
UPDATE organizations SET budget = 0 WHERE id IN (4, 5, 6, 7);
UPDATE organizations SET budget = 0 WHERE id IN (8, 9);WITH budget_allocation AS (SELECT o.id, o.parent_id, o.name, o.budget AS original_budget,o.budget AS allocated_budget, LEVEL AS org_levelFROM organizations oSTART WITH o.id = 1CONNECT BY PRIOR o.id = o.parent_id
)
SELECT ba.id, ba.parent_id, ba.name, ba.original_budget,CASEWHEN ba.original_budget = 0 THENNVL((LAG(ba.allocated_budget) OVER (ORDER BY ba.org_level DESC) / COUNT(*) OVER (PARTITION BY ba.parent_id)), 0)ELSE ba.allocated_budgetEND AS final_budget,ba.org_level
FROM budget_allocation ba;

SQL Server 遞歸查詢示例

  • 支持版本:SQL Server 2005 開始支持 WITH 子句,包括遞歸 CTE(Common Table Expressions)。
  • 示例:假設有一個員工表 Employees,結構為 (EmployeeID, Name, ManagerID)
WITH RECURSIVE EmployeeHierarchy AS (SELECT EmployeeID, Name, ManagerIDFROM EmployeesWHERE ManagerID IS NULLUNION ALLSELECT e.EmployeeID, e.Name, e.ManagerIDFROM Employees eINNER JOIN EmployeeHierarchy eh ON e.ManagerID = eh.EmployeeID
)
SELECT * FROM EmployeeHierarchy;

PostgreSQL 遞歸查詢示例

  • 支持版本:PostgreSQL 8.4 開始支持遞歸 CTE(WITH RECURSIVE)。
  • 示例:假設有一個表示菜單層級關系的表 menus,結構為 (menu_id, menu_name, parent_menu_id)
WITH RECURSIVE menu_hierarchy AS (SELECT menu_id, menu_name, parent_menu_idFROM menusWHERE parent_menu_id IS NULLUNION ALLSELECT m.menu_id, m.menu_name, m.parent_menu_idFROM menus mINNER JOIN menu_hierarchy mh ON m.parent_menu_id = mh.menu_id
)
SELECT * FROM menu_hierarchy;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/66194.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/66194.shtml
英文地址,請注明出處:http://en.pswp.cn/web/66194.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Rust自學】13.2. 閉包 Pt.2:閉包的類型推斷和標注

13.2.0. 寫在正文之前 Rust語言在設計過程中收到了很多語言的啟發,而函數式編程對Rust產生了非常顯著的影響。函數式編程通常包括通過將函數作為值傳遞給參數、從其他函數返回它們、將它們分配給變量以供以后執行等等。 在本章中,我們會討論 Rust 的一…

【JavaScript】比較運算符的運用、定義函數、if(){}...esle{} 語句

比較運算符 !><> < 自定義函數&#xff1a; function 函數名&#xff08;&#xff09;{ } 判斷語句&#xff1a; if(判斷){ }else if(判斷){ 。。。。。。 }else{ } 代碼示例&#xff1a; <!DOCTYPE html> <html> <head><meta charset&quo…

WOA-Transformer鯨魚算法優化編碼器時間序列預測(Matlab實現)

WOA-Transformer鯨魚算法優化編碼器時間序列預測&#xff08;Matlab實現&#xff09; 目錄 WOA-Transformer鯨魚算法優化編碼器時間序列預測&#xff08;Matlab實現&#xff09;預測效果基本介紹程序設計參考資料 預測效果 基本介紹 1.Matlab實現WOA-Transformer鯨魚算法優化編…

25/1/15 嵌入式筆記 初學STM32F108

GPIO初始化函數 GPIO_Ini&#xff1a;初始化GPIO引腳的模式&#xff0c;速度和引腳號 GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化GPIOA的引腳0 GPIO輸出控制函數 GPIO_SetBits&#xff1a;將指定的GPIO引腳設置為高電平 GPIO_SetBits(GPIOA, GPIO_Pin_0); // 將GPIO…

mac m4 安裝 node

brew install node // 安裝 node //安裝的路徑在&#xff1a; /opt/homebrew/bin/node brew install node14 // brew install node22 // 安裝指定版本 如果需要設置環境變量&#xff1a;通過&#xff1a; which node 查找路徑 export PATH"/usr/local/opt/…

haproxy+nginx網站架構,實現負載均衡實驗筆記

前提準備&#xff1a; 兩臺nginx&#xff0c;一臺haproxynginx1&#xff1a;192.168.180.120nginx2&#xff1a;192.168.180.130&#xff0c;NFShaproxy&#xff1a;192.168.180.110 nginx&#xff08;兩臺nginx的操作是一樣的&#xff09;&#xff1a; 1. 安裝nginx #先安…

【C++篇】紅黑樹的實現

目錄 前言&#xff1a; 一&#xff0c;紅黑樹的概念 1.1&#xff0c;紅黑樹的規則 1.2&#xff0c;紅黑樹的最長路徑 1.3&#xff0c;紅黑樹的效率分析 二&#xff0c;紅黑樹的實現 2.1&#xff0c;紅黑樹的結構 2.2&#xff0c;紅黑樹的插入 2.2.1&#xff0c;大致過程…

如何在谷歌瀏覽器中設置自定義安全警告

隨著網絡環境的日益復雜&#xff0c;瀏覽器的安全問題也愈發引人關注。谷歌瀏覽器作為一款廣泛使用的瀏覽器&#xff0c;其自定義安全警告功能為用戶提供了更加個性化和安全的瀏覽體驗。本文將詳細介紹如何在谷歌瀏覽器中設置自定義安全警告&#xff0c;幫助用戶更好地保護自己…

Spring 6 第1章——概述

一.Spring是什么 Spring是一款主流的Java EE輕量級&#xff08;體積小、不需要依賴其它組件&#xff09;開源框架Spring的目的是用于簡化Java企業級應用的開發難度和開發周期Spring的用途不僅限于服務端的開發&#xff0c;從簡單性、可測試性和松耦合的角度而言&#xff0c;任…

C語言預處理藝術:編譯前的魔法之旅

大家好&#xff0c;這里是小編的博客頻道 小編的博客&#xff1a;就愛學編程 很高興在CSDN這個大家庭與大家相識&#xff0c;希望能在這里與大家共同進步&#xff0c;共同收獲更好的自己&#xff01;&#xff01;&#xff01; 本文目錄 引言正文一、預處理的作用與流程&#xf…

基于Springboot + vue實現的旅游網站

&#x1f942;(???)您的點贊&#x1f44d;?評論&#x1f4dd;?收藏?是作者創作的最大動力&#x1f91e; &#x1f496;&#x1f4d5;&#x1f389;&#x1f525; 支持我&#xff1a;點贊&#x1f44d;收藏??留言&#x1f4dd;歡迎留言討論 &#x1f525;&#x1f525;&…

docker-compose和docker倉庫

一、docker-compose 1.概述 docker-compose是一個自動編排工具&#xff0c;可以根據dockerfile自動化部署docker容器。 主要功能 配置定義 使用YAML文件&#xff08;通常命名為docker - compose.yml&#xff09;來描述應用程序的服務、網絡和卷等配置。 容器編排 可以同時…

MAC AndroidStudio模擬器無網絡

先確認PC端是正常訪問網絡的&#xff1b; 模擬器端修改Wifi設置&#xff1a;設置 - 網絡和互聯網 - WALN設置 按照上圖修改&#xff1b; IP設置&#xff1a;從DHCP修改為靜態&#xff0c;IP地址&#xff1a;10.0.2.16 &#xff0c;網關&#xff1a;10.0.2.2 &#xff0c; DNS…

Wireshark 使用教程:網絡分析從入門到精通

一、引言 在網絡技術的廣闊領域中&#xff0c;網絡協議分析是一項至關重要的技能。Wireshark 作為一款開源且功能強大的網絡協議分析工具&#xff0c;被廣泛應用于網絡故障排查、網絡安全檢測以及網絡協議研究等諸多方面。本文將深入且詳細地介紹 Wireshark 的使用方法&#x…

Java 面試題 - ArrayList 和 LinkedList 的區別,哪個集合是線程安全的?

Java 面試題 - ArrayList 和 LinkedList 的區別&#xff0c;哪個集合是線程安全的&#xff1f; 在 Java 開發中&#xff0c;ArrayList和LinkedList是兩個常用的集合類&#xff0c;它們在數據結構和性能上有諸多不同&#xff0c;同時線程安全性也各有特點。深入理解這些差異&am…

nvim 打造成可用的IDE(2)

上一個 文章寫的太長了&#xff0c; 后來再寫東西 就一卡一卡的&#xff0c;所以新開一個。 主要是關于 bufferline的。 之前我的界面是這樣的。 這個圖標很不舒服有。 后來發現是在這里進行配置。 我也不知道&#xff0c;這個配置 我是從哪 抄過來的。 測試結果&#xff1…

升級 SpringBoot3 全項目講解 — 為什么 SpringBoot3 應該拋棄 Maven,搭配 Gradle 來使用?

學會這款 &#x1f525;全新設計的 Java 腳手架 &#xff0c;從此面試不再怕&#xff01; 隨著 Spring Boot 3 的發布&#xff0c;許多開發者開始考慮如何將現有項目升級到最新版本。Spring Boot 3 帶來了許多新特性&#xff0c;包括對 Java 17 的支持、更好的性能優化以及對 G…

Java學習筆記(二十三)

1 CacheEvict CacheEvict是Spring框架中用于清空緩存的注解。以下是對CacheEvict注解的詳細介紹&#xff1a; 1.1 作用 CacheEvict注解的主要作用是刪除緩存中的數據。在方法執行后或執行前&#xff08;根據配置&#xff09;&#xff0c;它可以清空指定的緩存項或整個緩存區…

如何優化Elasticsearch大文檔查詢?

記錄一次業務復雜場景下DSL優化的過程 背景 B端商城業務有一個場景就是客戶可見的產品列表是需要N多閘口及各種其它邏輯組合過濾的&#xff0c;各種閘口數據及產品數據都是存儲在ES的(有的是獨立索引&#xff0c;有的是作為產品屬性存儲在產品文檔上)。 在實際使用的過程中&a…

openCvSharp 計算機視覺圖片找茬

一、安裝包 <PackageReference Include"OpenCvSharp4" Version"4.10.0.20241108" /> <PackageReference Include"OpenCvSharp4.runtime.win" Version"4.10.0.20241108" /> 二、準備兩張圖片 三、編寫代碼 using OpenCv…