zoukankan      html  css  js  c++  java
  • Mysql 递归 查询所有父节点

    准备数据

    CREATE TABLE `demo` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `name` varchar(255) NOT NULL,
      `parent_id` int(11) NOT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
    
    INSERT INTO `demo` VALUES ('1', 'A', '0');
    INSERT INTO `demo` VALUES ('2', 'B', '1');
    INSERT INTO `demo` VALUES ('3', 'C', '1');
    INSERT INTO `demo` VALUES ('4', 'D', '2');
    INSERT INTO `demo` VALUES ('5', 'E', '4');
    INSERT INTO `demo` VALUES ('6', 'F', '1');
    INSERT INTO `demo` VALUES ('7', 'G', '1');
    id    name  parent_id 
    
    1        A        0
    2        B        1
    3        C        1
    4        D        2
    5        E        4
    6        F        1
    7        G        1

    需求:根据一个子ID,查询所有父类

    SELECT t2.id, t2.`name`
    FROM
        (
            SELECT 
            @r AS _id,
            (SELECT @r := parent_id FROM demo WHERE id = _id) AS parent_id,
            @l := @l + 1 AS lvl
            FROM
            (SELECT @r := 5, @l := 0) vars, demo AS h
            WHERE @r <> 0 
        ) t1
    JOIN demo t2
    ON t1._id = t2.Id

    结果如下

    id      name
    1         A
    2         B
    4         D
    5         E

    分析过程(答疑过程)

     我使用将SQL语句拆分的方式,并展示每句SQL运行结果,让各位可以看的更明白,以便于根据你自己的业务需求进行更改

     1、先不管T2,先把T1的SQL抽出来看

    SELECT 
            @r AS _id,
            (SELECT @r := parent_id FROM demo WHERE id = _id) AS parent_id,
            @l := @l + 1 AS lvl
            FROM
            (SELECT @r := 5, @l := 0) vars, demo AS h
            WHERE @r <> 0 

    从这里可以看出,其实T1已经找出ID为5节点的所有父节点了,和T2(即demo表)进行左连接,只不过是为了根据Id获取Name而已。而且还可以看出@l其实在整个SQL中并没有什么作用,只是用来标识节点的等级,底级子节点的lvl为1,父节点lvl值越大表示越靠近顶级父节点,想象一下树结构,你就明白了

     2、搞明白@r := 5 

    SELECT @r := 5, @l := 0

    从这里看出,其实@r,@l 就是一个变量而已,
    作为变量自然你可以随便起名,当然也可以随便赋值,改成@a,@b也都是可以的
    那这句SQL的意思就出来了,它表示给变量@r赋值,值为5,给@l赋值,值为0
    因此,其实整个SQL的意思也明了了就是根据子ID5,查询所有父类

    3、搞懂变量@r 值如何变化

    @r AS _id,
            (SELECT @r := parent_id FROM demo WHERE id = _id) AS parent_id

    根据MYSQL执行顺序(在文章末尾处),SQL语句在执行时会先执行From,即会先执行上一步的赋值操作,因此这里的_id值为5,所以上面的SQL等同于如下SQL

    4、所以上面的SQL等同于如下SQL

    SELECT @r := parent_id FROM demo WHERE id = 5

    这句SQL肯定没人不会,但是有一个点很重要,它是整个执行过程的核心,就是@r := parent_id,它在查询Id为5的节点的父Id时,把这个父Id同时赋给了变量@r。因此@r值改变了,它从5变为了4。

    这个时候思路就很清晰了,只要我们设置限制条件,让SQL在@r为0的时候结束循环就OK了

    5、<>符号的含义

    WHERE @r <> 0

    这个就没什么好说的了,<>符号在MYSQL中表示不等于,这就是我们在第四步中所说的限制条件,它限制了@r这个变量不能等于0。所以当@r不等于0时,SQL语句会根据子ID向上查询父ID,又把父ID当做子ID赋值给@r,再次向上查询,直至@r变量的值为0为止。

    补充:如果业务不需要,可以完全可以去掉@l变量

     

    SELECT@a AS _id, 
            (SELECT @a := parent_id FROM demo WHERE id = _id) AS parent_id
        FROM 
            (SELECT @a := 5) vars, 
            demo h 
        WHERE @a <> 0

    可以看出@l其实在整个SQL中并没有什么作用,只是用来标识节点的等级,这也证实了我们上述第一步的关于@l的解释

    因为这个过程很类似于JAVA通过构造树结构向上递归查询的方式,所以这个SQL形式被很多人叫做MYSQL递归查询

    关于MYSQL的查询顺序

    1.FROM
    2.ON
    3.JOIN
    4.WHERE
    5.GROUP By
    6.CUBE|ROllUP
    7.HAVING
    8.SELECT
    9.DISTINCT
    10.ORDER BY
    11.LIMIT
    最先执行的是FROM操作,最后执行的是LIMIT操作。每个操作都会产生一个虚拟表,该虚拟表作为一个处理的输入

  • 相关阅读:
    利用Python进行数据分析笔记-时间序列(时区、周期、频率)
    形象易懂讲解算法I——小波变换
    小波变换与傅里叶变换的区别
    Thinkpad E550 开启 Legacy Only
    Thinkpad E550 开启 虚拟化
    常见音频接口
    IAR embedded Workbench for ARM 8.32.1 安装包
    stm32f767 无操作系统 LwIP 移植 (一)
    stm32f767 无操作系统 LwIP 移植 (二)
    北京市电力公司
  • 原文地址:https://www.cnblogs.com/liuxiaoji/p/15219091.html
Copyright © 2011-2022 走看看