【Sql递归查询】Mysql、Oracle、SQL Server、PostgreSQL 实现递归查询的区别与案例(详解)

news2025/1/17 22:20:19

在这里插入图片描述

文章目录

    • 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);

使用自连接查询所有部门及其子部门:

在这里插入图片描述

SELECT
    t1.id AS root_id,
    t1.name AS root_name,
    t2.id AS child_id,
    t2.name AS child_name
FROM
    departments t1
JOIN
    departments t2
ON
    t1.id = t2.parent_id
UNION
SELECT
    id AS root_id,
    name AS root_name,
    id AS child_id,
    name AS child_name
FROM
    departments
WHERE
    parent_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,用于标记游标是否已经完成遍历,初始值为 FALSE
    DECLARE done INT DEFAULT FALSE;
    -- 声明一个整数变量 v_id,用于存储从游标中获取的部门 id
    DECLARE v_id INT;
    -- 声明一个字符串变量 v_name,用于存储从游标中获取的部门名称
    DECLARE v_name VARCHAR(50);
    -- 声明一个游标 cur,用于查询 departments 表中 parent_id 等于 p_parent_id 的记录
    DECLARE cur CURSOR FOR
        SELECT id, name FROM departments WHERE parent_id = p_parent_id;
    -- 声明一个继续处理程序,当游标没有更多数据时,将 done 置为 TRUE
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

    -- 创建一个临时表 temp_departments,用于存储递归调用的结果
    -- 仅在该表不存在时创建,包含三个列:id、name 和 level
    CREATE 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 THEN
            LEAVE 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,层级加 1
        SET @result = recursive_departments_func(v_id, p_level + 1);
    END LOOP;

    -- 关闭游标 cur
    CLOSE cur;

    -- 函数最终返回 1
    RETURN 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 (
    -- 初始查询,找到CEO
    SELECT employee_id, name, manager_id
    FROM employees
    WHERE name = '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
)
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 1
    SELECT employee_id, name, manager_id
    FROM employees
    WHERE name = 'Developer 1'
    UNION ALL
    -- 递归部分,找到上级员工
    SELECT e.employee_id, e.name, e.manager_id
    FROM employees e
    INNER 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 depth
    FROM organizations
    WHERE id = 1
    UNION ALL
    -- 递归部分:选择子组织,深度加 1
    SELECT o.id, o.parent_id, o.name, o.level, oh.depth + 1
    FROM organizations o
    JOIN 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_id
    FROM departments
    WHERE parent_department_id IS NULL
    UNION ALL
    SELECT d.department_id, d.department_name, d.parent_department_id
    FROM departments d
    INNER 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_level
    FROM organizations o
    START WITH o.id = 1
    CONNECT 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_level
    FROM organizations o
    START WITH o.id = 1
    CONNECT BY PRIOR o.id = o.parent_id
)
SELECT ba.id, ba.parent_id, ba.name, ba.original_budget,
       CASE
           WHEN ba.original_budget = 0 THEN
               NVL((LAG(ba.allocated_budget) OVER (ORDER BY ba.org_level DESC) / COUNT(*) OVER (PARTITION BY ba.parent_id)), 0)
           ELSE ba.allocated_budget
       END 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, ManagerID
    FROM Employees
    WHERE ManagerID IS NULL
    UNION ALL
    SELECT e.EmployeeID, e.Name, e.ManagerID
    FROM Employees e
    INNER 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_id
    FROM menus
    WHERE parent_menu_id IS NULL
    UNION ALL
    SELECT m.menu_id, m.menu_name, m.parent_menu_id
    FROM menus m
    INNER JOIN menu_hierarchy mh ON m.parent_menu_id = mh.menu_id
)
SELECT * FROM menu_hierarchy;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2278176.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue2修改表单只提交被修改的数据的字段传给后端接口

效果: 步骤一、 vue2修改表单提交的时候,只将修改的数据的字段传给后端接口,没有修改得数据不传参给接口。 在 data 对象中添加一个新的属性,用于存储初始表单数据的副本,与当前表单数据进行比较,找出哪些…

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程(配套案例数据)》专栏上线了

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程》全新上线了,欢迎广大GISer朋友关注,一起探索GIS奥秘,分享GIS价值! 本专栏以实战案例的形式,深入浅出地介绍了GRASS GIS的基本使用方法,用一个个实例讲…

纯命令 git使用

首先我们到一个新的公司 要添加一个新的git仓库的权限 我们应该现拉去代码 配置git的仓库信息 第一 先添加权限 第二 如果不是自己电脑 需要配置信息 配置基础信息 查看本地git账号git config --global user.name git config --global user.email修改本地账号git co…

Linux系统编程:深入理解计算机软硬件体系和架构

一、硬件体系 首先我们要知道,我们最常见的计算机(笔记本)以及我们不常见的计算机(服务器)其实本质上都是一堆硬件的结合:cpu、网卡、显卡、内存、磁盘、显示器、键盘…… 但他们并不是毫无章法地放在一起…

微信小程序-Docker+Nginx环境配置业务域名验证文件

在实际开发或运维工作中,我们时常需要在 Nginx 部署的服务器上提供一个特定的静态文件,用于域名验证或第三方平台验证。若此时使用 Docker 容器部署了 Nginx,就需要将该验证文件正确地映射(挂载)到容器中,并…

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (五、电影详情页的设计实现)

在上一篇文章中,完成了电影列表页的开发。接下来,将进入电影详情页的设计实现阶段。这个页面将展示电影的详细信息,包括电影海报、评分、简介以及相关影人等。将使用 HarmonyOS 提供的常用组件,并结合第三方库 nutpi/axios 来实现…

在 macOS 上,用命令行连接 MySQL(/usr/local/mysql/bin/mysql -u root -p)

根据你提供的文件内容,MySQL 的安装路径是 /usr/local/mysql。要直接使用 mysql 命令,你需要找到 mysql 可执行文件的路径。 在 macOS 上,mysql 客户端通常位于 MySQL 安装目录的 bin 子目录中。因此,完整的路径应该是&#xff1…

使用docker-compose安装ELK(elasticsearch,logstash,kibana)并简单使用

首先服务器上需要安装docker已经docker-compose,如果没有,可以参考我之前写的文章进行安装。 https://blog.csdn.net/a_lllk/article/details/143382884?spm1001.2014.3001.5502 1.下载并启动elk容器 先创建一个网关,让所有的容器共用此网…

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题

解决“无法定位程序输入点 av_buffer_create 于动态链接库 XXX\Obsidian.exe 上”问题 问题描述 本人在使用zotero中的zotero one(青柠学术插件)的时候,使用插件跳转obsidian中的对应笔记,出现上图情况。(错误中提到的…

《小迪安全》学习笔记05

目录 读取: 写入: (其中的读取和写入时我认为比较重要的,所以单独做成了目录,这里的读取和写入是指在进行sql注入的时候与本地文件进行的交互) 好久没发博客了。。。从这篇开始的小迪安全学习笔记就开始…

Jupyter notebook中运行dos指令运行方法

Jupyter notebook中运行dos指令运行方法 目录 Jupyter notebook中运行dos指令运行方法一、DOS(磁盘操作系统)指令介绍1.1 DOS介绍1.2 DOS指令1.2.1 DIR - 显示当前目录下的文件和子目录列表。1.2.2 CD 或 CHDIR - 改变当前目录1.2.3 使用 CD .. 可以返回上一级目录1…

excel 判断某个单元格的日期,如果超过3天,则在另一个单元格显示超过三天的公式

excel 判断某个单元格的日期&#xff0c;如果超过3天&#xff0c;则在另一个单元格显示超过三天的公式&#xff0c;公式如下&#xff1a; IF(DATEDIF(C627,TODAY(),"d")<4,"3天以内","超过三天") IF(D627"超过3天","文件赶紧…

内存与缓存:保姆级图文详解

文章目录 前言1、计算机存储设备1.1、硬盘、内存、缓存1.2、金字塔结构1.3、数据流通过程 2、数据结构内存效率3、数据结构缓存效率 前言 亲爱的家人们&#xff0c;创作很不容易&#xff0c;若对您有帮助的话&#xff0c;请点赞收藏加关注哦&#xff0c;您的关注是我持续创作的…

eBay账号安全攻略:巧妙应对风险

在跨境电商的浪潮中&#xff0c;eBay宛如一座璀璨的灯塔&#xff0c;照亮了无数买卖双方的交易之路。但别忘了&#xff0c;网络安全的阴霾也在悄然蔓延&#xff0c;让eBay账号时刻处于黑客攻击、数据泄露、钓鱼诈骗等风险的阴影之下。别担心&#xff0c;今天就来为你支支招&…

宝塔php7.4安装报错,无法安装,php8以上可以安装,以下的不行,gd库什么的都正常

宝塔的依赖问题导致的问题&#xff0c;最后手动挂载后才解决。。。废了三天三夜终于搞好了。。。。无语&#xff5e; 建议&#xff1a;不要一直升级宝塔版本&#xff0c;升级前备份或者开服务商的实例镜像&#xff0c;方便恢复&#xff0c;不然&#xff0c;可就GG了&#xff5…

【大数据】机器学习 -----关于data.csv数据集分析案例

打开表 import pandas as pd df2 pd.read_csv("data.csv",encoding"gbk") df2.head()查看数据属性&#xff08;列标题&#xff0c;表形状&#xff0c;类型&#xff0c;行标题&#xff0c;值&#xff09; print("列标题:",df2.columns)Data…

【数据可视化-12】数据分析岗位招聘分析

&#x1f9d1; 博主简介&#xff1a;曾任某智慧城市类企业算法总监&#xff0c;目前在美国市场的物流公司从事高级算法工程师一职&#xff0c;深耕人工智能领域&#xff0c;精通python数据挖掘、可视化、机器学习等&#xff0c;发表过AI相关的专利并多次在AI类比赛中获奖。CSDN…

RabbitMQ故障全解析:消费、消息及日常报错处理与集群修复

文章目录 前言&#xff1a;1 消费慢2 消息丢失3 消息重复消费4 日常报错及解决4.1 报错“error in config file “/etc/rabbitmq/rabbitmq.config” (none): no ending found”4.2 生产者发送消息报错4.3 浏览器打开IP地址&#xff0c;无法访问 RabbitMQ&#xff08;白屏没有结…

网络安全——常用语及linux系统

一、网络安全概念及法规 网络安全&#xff1a;网络空间安全 cyber security 信息系统&#xff1a;由计算机硬件、网络和通信设备、计算机软件、信息资源、信息用户和规章制度组成的已处理信息流为目的的人机一体化系统 信息系统安全三要素&#xff08;CIA&#xff09; 保密…

00_专栏《Redis 7.x企业级开发实战教程》介绍

大家好,我是袁庭新。Redis作为一款高性能、多用途的内存数据库,凭借其丰富的数据结构、高速读写能力、原子操作特性及发布订阅等功能,在缓存加速、分布式锁、消息队列等场景中不可或缺,极大提升了系统性能与开发效率,是现代互联网应用架构的关键组件。 你是否在学习Redis…