【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.mzph.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…

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…

如何优化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…

实战:FRP内网穿透部署-支持ssh、web访问

目录 1 准备工作2 公网服务器部署server端2.1 frps.ini配置 3 内网客户端部署client端3.1 frpc.ini配置&#xff08;内网服务器01&#xff09;3.2 frpc.ini配置&#xff08;内网服务器02&#xff09; 4 服务启动脚本4.1 公网服务器 server4.2 内网服务器 client 2 systemctl常见…

Uniapp中实现加载更多、下拉刷新、返回顶部功能

一、加载更多&#xff1a; 在到达底部时&#xff0c;将新请求过来的数据追加到原来的数组即可&#xff1a; import {onReachBottom } from "dcloudio/uni-app";const pets ref([]); // 显示数据function network() {uni.request({url: "https://api.thecatap…

Flutter:封装ActionSheet 操作菜单

演示效果图 action_sheet_util.dart import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import package:demo/common/index.dart;class ActionSheetUtil {/// 底部操作表/// [context] 上下文/// [title] 标题/// [items] 选项列表 …