【问题标题】:Connect By Prior Equivalent for MySQLMySQL 的先验等价物连接
【发布时间】:2019-04-09 20:15:38
【问题描述】:

全部,

我在一个表中有三个字段,它们定义了 MySQL 数据库版本 5.0 中存在的父子关系。表名是 tb_Tree,它有以下数据:

Table Name: tb_Tree

Id | ParentId | Name
--------------------
1  | 0        | Fruits
2  | 0        | Vegetables
3  | 1        | Apple
4  | 1        | Orange
5  | 2        | Cabbage
6  | 2        | Eggplant

如果指定了 ParentId,我如何编写查询以获取所有子项。请注意,给出的表条目只是示例数据,它们可以有更多行。 Oracle 有一个“CONNECT BY PRIOR”子句,但我没有为 MySQL 找到类似的东西。谁能给点建议?

谢谢

【问题讨论】:

  • 我认为“WITH”可以帮助您进行递归查询。
  • 您能详细说明一下吗?谢谢
  • MySQL 没有 CTE (WITH)
  • MySQL 不支持任何类型的递归查询(Oracle 的 connect by 和标准的递归公用表表达式都不支持)。

标签: mysql sql stored-procedures


【解决方案1】:

MySQL 不支持递归查询,所以你必须努力做到这一点:

  1. 选择ParentID = X 所在的行,其中X 是您的根。
  2. 从 (1) 中收集 Id 值。
  3. 对 (2) 中的每个 Id 重复 (1)。
  4. 继续手动递归,直到找到所有叶节点。

如果您知道最大深度,那么您可以将表连接到自身(使用 LEFT OUTER JOIN)到最大可能深度,然后清除 NULL。

您还可以将树表示更改为nested sets

【讨论】:

    【解决方案2】:

    可能会迟到。

    在 MySQL8 中,您可以通过递归子句来实现。这是一个例子。

     with recursive cte (id, name, parent_id) as (
      select     id,
                 name,
                 parent_id
      from       products
      where      parent_id = 19
      union all
      select     p.id,
                 p.name,
                 p.parent_id
      from       products p
      inner join cte
              on p.parent_id = cte.id
    )
    select * from cte;
    

    如需更多帮助,请找到另一个thread,希望它对某人有所帮助。

    【讨论】:

      【解决方案3】:

      你也可以看看这个有趣的博客,它演示了我们如何在 mysql 中得到类似的结果

      http://explainextended.com/2009/03/17/hierarchical-queries-in-mysql/

      【讨论】:

        【解决方案4】:

        这是一个旧线程,但由于我在另一个论坛上得到了这个问题,所以我想我会在这里添加它。对于这种情况,我创建了一个硬编码的存储过程来处理特定情况。这当然有一些缺点,因为并非所有用户都可以随意创建存储过程。

        考虑下表中的节点和子节点:

        CREATE TABLE nodes (
               parent INT,
               child INT
        );
        
        INSERT INTO nodes VALUES
               ( 5,  2), ( 5, 3),
               (18, 11), (18, 7),
               (17,  9), (17, 8),
               (26, 13), (26, 1), (26,12),
               (15, 10), (15, 5),       
               (38, 15), (38, 17), (38, 6),
               (NULL, 38), (NULL, 26), (NULL, 18);
        

        使用此表,以下存储过程将计算一个包含所提供节点的所有后代的结果集:

        delimiter $$
        CREATE PROCEDURE find_parts(seed INT)
        BEGIN
          -- Temporary storage
          DROP TABLE IF EXISTS _result;
          CREATE TEMPORARY TABLE _result (node INT PRIMARY KEY);
        
          -- Seeding
          INSERT INTO _result VALUES (seed);
        
          -- Iteration
          DROP TABLE IF EXISTS _tmp;
          CREATE TEMPORARY TABLE _tmp LIKE _result;
          REPEAT
            TRUNCATE TABLE _tmp;
            INSERT INTO _tmp SELECT child AS node
              FROM _result JOIN nodes ON node = parent;
        
            INSERT IGNORE INTO _result SELECT node FROM _tmp;
          UNTIL ROW_COUNT() = 0
          END REPEAT;
          DROP TABLE _tmp;
          SELECT * FROM _result;
        END $$
        delimiter ;
        

        【讨论】:

          【解决方案5】:

          下面的select 列出了所有植物及其parentid 最多4 级(当然你可以扩展级别):

          select id, name, parentid
          ,(select parentid from tb_tree where id=t.parentid) parentid2
          ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
          ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4 
          from tb_tree t
          

          然后您可以使用此查询来获得最终结果。例如,您可以通过以下 sql 获取“Fruits”的所有子项:

          select id ,name from (
              select id, name, parentid
              ,(select parentid from tb_tree where id=t.parentid) parentid2
              ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid)) parentid3
              ,(select parentid from tb_tree where id=(select parentid from tb_tree where id=(select parentid from tb_tree where id=t.parentid))) parentid4 
              from tb_tree t) tt
          where ifnull(parentid4,0)=1 or ifnull(parentid3,0)=1 or ifnull(parentid2,0)=1 or ifnull(parentid,0)=1
          

          【讨论】:

            【解决方案6】:

            下面的存储过程订购了一个表,该表的行具有对前一个的反向引用。请注意,第一步我将行复制到临时表中 - 这些行符合某些条件。在我的情况下,这些行属于同一线性(GPS 导航中使用的道路)。业务领域并不重要。就我而言,我正在对属于同一条道路的路段进行排序

            如果存在 orderLocations,则删除过程; 分隔符 //

            创建过程 orderLocations(_full_linear_code VARCHAR(11)) 开始

            DECLARE _code VARCHAR(11);
            DECLARE _id INT(4);
            DECLARE _count INT(4);
            DECLARE _pos INT(4);
            
            DROP TEMPORARY TABLE IF EXISTS temp_sort;
            
            CREATE TEMPORARY TABLE temp_sort (
              id              INT(4) PRIMARY KEY,
              pos             INT(4),
              code            VARCHAR(11),
              prev_code       VARCHAR(11)
            );
            
            -- copy all records to sort into temp table - this way sorting would go all in memory
            INSERT INTO temp_sort SELECT
                                     id, -- this is primary key of original table
                                     NULL, -- this is position that still to be calculated
                                     full_tmc_code, -- this is a column that references sorted by
                                     negative_offset -- this is a reference to the previous record (will be blank for the first)
                                   FROM tmc_file_location
                                   WHERE linear_full_tmc_code = _full_linear_code;
            
            -- this is how many records we have to sort / update position
            SELECT count(*)
            FROM temp_sort
            INTO _count;
            
            -- first position index
            SET _pos = 1;
            
            -- pick first record that has no prior record
            SELECT
              code,
              id
            FROM temp_sort l
            WHERE prev_code IS NULL
            INTO _code, _id;
            
            -- update position of the first record
            UPDATE temp_sort
            SET pos = _pos
            WHERE id = _id;
            
            -- all other go by chain link
            WHILE (_pos < _count) DO
              SET _pos = _pos +1;
            
              SELECT
                code,
                id
              FROM temp_sort
              WHERE prev_code = _code
              INTO _code, _id;
            
            
              UPDATE temp_sort
              SET pos = _pos
              WHERE id = _id;
            
            END WHILE;
            
            -- join two tables and return position along with all other fields
            SELECT
              t.pos,
              l.*
            FROM tmc_file_location l, temp_sort t
            WHERE t.id = l.id
            ORDER BY t.pos;
            
            END;
            

            【讨论】:

              猜你喜欢
              • 2017-07-05
              • 2023-03-05
              • 1970-01-01
              • 1970-01-01
              • 2011-10-15
              • 2021-11-08
              • 1970-01-01
              • 2023-04-05
              • 2011-02-13
              相关资源
              最近更新 更多