복붙노트

[SQL] MySQL의 계층 적 재귀 쿼리 만드는 방법

SQL

MySQL의 계층 적 재귀 쿼리 만드는 방법

나는 다음과 MySQL의 테이블을 가지고 :

id | name        | parent_id
19 | category1   | 0
20 | category2   | 19
21 | category3   | 20
22 | category4   | 21
......

지금, 나는 단순히 ID를 제공하는 단일 MySQL의 쿼리 갖고 싶어 [인스턴스 말에 대한 '를 ID = 19'] 나는 모든 자식 ID를 얻어야한다 [즉, 결과 ....] IDS '20, 21, 22을 가져야 또한, 아이들의 계층 구조가 다를 수 있습니다 알려져 있지 않다 ....

또한, 나는 이미 ..... for 루프를 사용하여 솔루션을 나에게 하나의 MySQL의 쿼리 가능한 경우를 사용하여 동일한을 달성하는 방법을 알려주세요.

해결법

  1. ==============================

    1.MySQL을위한 8 + : 구문 재귀를 사용합니다. MySQL의 5.x의 경우 : 사용 인라인 변수, 경로 ID를, 또는 자체 조인.

    MySQL을위한 8 + : 구문 재귀를 사용합니다. MySQL의 5.x의 경우 : 사용 인라인 변수, 경로 ID를, 또는 자체 조인.

    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;
    

    PARENT_ID = 19에 지정된 값은 당신의 모든 자손을 선택하려는 부모의 ID로 설정해야합니다.

    MySQL의 버전 공통 테이블 표현식 (버전 5.7까지), 다음 쿼리와이를 달성 할 지원하지 않는 :

    select  id,
            name,
            parent_id 
    from    (select * from products
             order by parent_id, id) products_sorted,
            (select @pv := '19') initialisation
    where   find_in_set(parent_id, @pv)
    and     length(@pv := concat(@pv, ',', id))
    

    여기에 바이올린입니다.

    여기서 @pv에 지정된 값 : = '19'당신의 모든 자손을 선택하려는 부모의 ID로 설정해야합니다.

    부모가 여러 자식이 경우에도 작동합니다. 그러나, 그렇지 않으면 결과가 완료되지 않습니다, 각 레코드는 조건 PARENT_ID

    이 쿼리는 특정 MySQL의 구문을 사용 변수에 할당하고 실행 중에 수정됩니다. 일부 가정은 실행 순서에 대해 만들어집니다 :

    모두 모두, 하나에 의존하는 너무 위험 이러한 가정을 찾을 수 있습니다. 문서는 경고 :

    그것은 위의 쿼리와 함께 지속적으로 작동 그럼에도 불구하고, 평가 순서는 여전히 조건을 추가 할 때 예를 들어, 변경하거나 더 큰 쿼리에서보기 또는 하위 쿼리로이 쿼리를 사용할 수 있습니다. 그것은 미래 MySQL의 릴리스에서 제거 될 것 "기능"입니다 :

    MySQL은 8.0에서, 위에서 언급 한 바와 같이 이후 당신은 구문으로 재귀를 사용해야합니다.

    FIND_IN_SET 작업이 반환 된 레코드의 수와 동일한 정도의 크기의 크기에 도달 목록에 확실히, 목록에서 번호를 찾을 수있는 가장 이상적인 방법하지 아니므로 매우 큰 데이터 세트의이 솔루션은, 느린 얻을 수 있습니다 .

    재귀 쿼리 (예 : 포스트 그레스 8.4+, SQL 서버 2005 +, DB2, 오라클 11gR2 +, SQLite는 3.8.4+, 파이어 버드 2.1, H2, HyperSQL 2.1 1999 ISO 표준 [RECURSIVE] 구문 : 점점 더 많은 데이터베이스 SQL을 구현합니다. 0+, 테라 데이타, MariaDB 10.2.2+). 그리고 버전 8.0로, 또한 MySQL은 그것을 지원합니다. 사용하는 구문이 답변의 상단을 참조하십시오.

    일부 데이터베이스는 오라클, DB2, 인포믹스, CUBRID와 다른 데이터베이스에서 사용할 수있는 CONNECT BY 절과 같은 계층보기 - 업을위한 대안, 비표준 문법을 가지고있다.

    MySQL 버전 5.7은 이러한 기능을 제공하지 않습니다. 데이터베이스 엔진이 구문을 제공하거나이하는 일에 마이그레이션 할 수 있습니다 때, 그 갈 수있는 최선의 선택은 분명하다. 그렇지 않다면, 또한 다음과 같은 대안을 고려하십시오.

    경로 : 당신이 계층 정보가 포함 된 ID 값을 할당 할 경우 상황이 훨씬 쉬워집니다. 예를 들어, 귀하의 경우이 다음과 같이 수 :

    ID       | NAME
    19       | category1   
    19/1     | category2  
    19/1/1   | category3  
    19/1/1/1 | category4  
    

    그런 다음 선택은 다음과 같이 보일 것이다 :

    select  id,
            name 
    from    products
    where   id like '19/%'
    

    당신이 당신의 계층 구조 트리가 될 수있는 방법을 깊이에 대한 상한을 알고 있다면, 당신은이 같은 표준 SQL 쿼리를 사용할 수 있습니다 :

    select      p6.parent_id as parent6_id,
                p5.parent_id as parent5_id,
                p4.parent_id as parent4_id,
                p3.parent_id as parent3_id,
                p2.parent_id as parent2_id,
                p1.parent_id as parent_id,
                p1.id as product_id,
                p1.name
    from        products p1
    left join   products p2 on p2.id = p1.parent_id 
    left join   products p3 on p3.id = p2.parent_id 
    left join   products p4 on p4.id = p3.parent_id  
    left join   products p5 on p5.id = p4.parent_id  
    left join   products p6 on p6.id = p5.parent_id
    where       19 in (p1.parent_id, 
                       p2.parent_id, 
                       p3.parent_id, 
                       p4.parent_id, 
                       p5.parent_id, 
                       p6.parent_id) 
    order       by 1, 2, 3, 4, 5, 6, 7;
    

    이 바이올린을 참조하십시오

    당신의 후손을 검색하려는 부모를 Where 조건 지정합니다. 필요에 따라 더 수준이 쿼리를 확장 할 수 있습니다.

  2. ==============================

    2.MySQL의 계층 적 데이터 관리 블로그에서

    MySQL의 계층 적 데이터 관리 블로그에서

    테이블 구조

    +-------------+----------------------+--------+
    | category_id | name                 | parent |
    +-------------+----------------------+--------+
    |           1 | ELECTRONICS          |   NULL |
    |           2 | TELEVISIONS          |      1 |
    |           3 | TUBE                 |      2 |
    |           4 | LCD                  |      2 |
    |           5 | PLASMA               |      2 |
    |           6 | PORTABLE ELECTRONICS |      1 |
    |           7 | MP3 PLAYERS          |      6 |
    |           8 | FLASH                |      7 |
    |           9 | CD PLAYERS           |      6 |
    |          10 | 2 WAY RADIOS         |      6 |
    +-------------+----------------------+--------+
    

    질문:

    SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4
    FROM category AS t1
    LEFT JOIN category AS t2 ON t2.parent = t1.category_id
    LEFT JOIN category AS t3 ON t3.parent = t2.category_id
    LEFT JOIN category AS t4 ON t4.parent = t3.category_id
    WHERE t1.name = 'ELECTRONICS';
    

    산출

    +-------------+----------------------+--------------+-------+
    | lev1        | lev2                 | lev3         | lev4  |
    +-------------+----------------------+--------------+-------+
    | ELECTRONICS | TELEVISIONS          | TUBE         | NULL  |
    | ELECTRONICS | TELEVISIONS          | LCD          | NULL  |
    | ELECTRONICS | TELEVISIONS          | PLASMA       | NULL  |
    | ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS  | FLASH |
    | ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS   | NULL  |
    | ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL  |
    +-------------+----------------------+--------------+-------+
    

    한 번에 또는 다른에서 대부분의 사용자는 SQL 데이터베이스에서 계층 적 데이터 처리 및 의심의 여지가 계층 적 데이터의 관리는 관계형 데이터베이스를위한 것입니다 무엇 아니라는 것을 배운 적이있다. 관계형 데이터베이스의 테이블 (XML과 같은) 계층이 아니라 단순히 단순 목록입니다. 계층 적 데이터는 자연적으로 관계형 데이터베이스 테이블에 표시되지 않은 부모 - 자식 관계가 있습니다. 자세히보기

    자세한 내용은 블로그를 참조하십시오.

    편집하다:

    select @pv:=category_id as category_id, name, parent from category
    join
    (select @pv:=19)tmp
    where parent=@pv
    

    산출:

    category_id name    parent
    19  category1   0
    20  category2   19
    21  category3   20
    22  category4   21
    

    참조 : 어떻게 MySQL의에서 재귀 SELECT 쿼리를 할까?

  3. ==============================

    3.다음을 시도해보십시오

    다음을 시도해보십시오

    표 정의 :

    DROP TABLE IF EXISTS category;
    CREATE TABLE category (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(20),
        parent_id INT,
        CONSTRAINT fk_category_parent FOREIGN KEY (parent_id)
        REFERENCES category (id)
    ) engine=innodb;
    

    실험 행 :

    INSERT INTO category VALUES
    (19, 'category1', NULL),
    (20, 'category2', 19),
    (21, 'category3', 20),
    (22, 'category4', 21),
    (23, 'categoryA', 19),
    (24, 'categoryB', 23),
    (25, 'categoryC', 23),
    (26, 'categoryD', 24);
    

    저장 프로 시저를 재귀 :

    DROP PROCEDURE IF EXISTS getpath;
    DELIMITER $$
    CREATE PROCEDURE getpath(IN cat_id INT, OUT path TEXT)
    BEGIN
        DECLARE catname VARCHAR(20);
        DECLARE temppath TEXT;
        DECLARE tempparent INT;
        SET max_sp_recursion_depth = 255;
        SELECT name, parent_id FROM category WHERE id=cat_id INTO catname, tempparent;
        IF tempparent IS NULL
        THEN
            SET path = catname;
        ELSE
            CALL getpath(tempparent, temppath);
            SET path = CONCAT(temppath, '/', catname);
        END IF;
    END$$
    DELIMITER ;
    

    스토어드 프로 시저의 기능 래퍼 :

    DROP FUNCTION IF EXISTS getpath;
    DELIMITER $$
    CREATE FUNCTION getpath(cat_id INT) RETURNS TEXT DETERMINISTIC
    BEGIN
        DECLARE res TEXT;
        CALL getpath(cat_id, res);
        RETURN res;
    END$$
    DELIMITER ;
    

    선택 예 :

    SELECT id, name, getpath(id) AS path FROM category;
    

    산출:

    +----+-----------+-----------------------------------------+
    | id | name      | path                                    |
    +----+-----------+-----------------------------------------+
    | 19 | category1 | category1                               |
    | 20 | category2 | category1/category2                     |
    | 21 | category3 | category1/category2/category3           |
    | 22 | category4 | category1/category2/category3/category4 |
    | 23 | categoryA | category1/categoryA                     |
    | 24 | categoryB | category1/categoryA/categoryB           |
    | 25 | categoryC | category1/categoryA/categoryC           |
    | 26 | categoryD | category1/categoryA/categoryB/categoryD |
    +----+-----------+-----------------------------------------+
    

    특정 경로 행을 필터링 :

    SELECT id, name, getpath(id) AS path FROM category HAVING path LIKE 'category1/category2%';
    

    산출:

    +----+-----------+-----------------------------------------+
    | id | name      | path                                    |
    +----+-----------+-----------------------------------------+
    | 20 | category2 | category1/category2                     |
    | 21 | category3 | category1/category2/category3           |
    | 22 | category4 | category1/category2/category3/category4 |
    +----+-----------+-----------------------------------------+
    
  4. ==============================

    4.나는이 함께 왔어요 가장 좋은 방법

    나는이 함께 왔어요 가장 좋은 방법

    리니지 접근 DESCR. 예를 들어, 어디든지 찾아 낼 수있다 이곳까지 또는 여기에. 나를 enspired 것입니다 - 함수를 사용합니다.

    결국 - 더 - 또는 - 덜 상대적으로 빠르고, 간단, 간단한 솔루션을 얻었다.

    기능의 몸

    -- --------------------------------------------------------------------------------
    -- Routine DDL
    -- Note: comments before and after the routine body will not be stored by the server
    -- --------------------------------------------------------------------------------
    DELIMITER $$
    
    CREATE DEFINER=`root`@`localhost` FUNCTION `get_lineage`(the_id INT) RETURNS text CHARSET utf8
        READS SQL DATA
    BEGIN
    
     DECLARE v_rec INT DEFAULT 0;
    
     DECLARE done INT DEFAULT FALSE;
     DECLARE v_res text DEFAULT '';
     DECLARE v_papa int;
     DECLARE v_papa_papa int DEFAULT -1;
     DECLARE csr CURSOR FOR 
      select _id,parent_id -- @n:=@n+1 as rownum,T1.* 
      from 
        (SELECT @r AS _id,
            (SELECT @r := table_parent_id FROM table WHERE table_id = _id) AS parent_id,
            @l := @l + 1 AS lvl
        FROM
            (SELECT @r := the_id, @l := 0,@n:=0) vars,
            table m
        WHERE @r <> 0
        ) T1
        where T1.parent_id is not null
     ORDER BY T1.lvl DESC;
     DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
        open csr;
        read_loop: LOOP
        fetch csr into v_papa,v_papa_papa;
            SET v_rec = v_rec+1;
            IF done THEN
                LEAVE read_loop;
            END IF;
            -- add first
            IF v_rec = 1 THEN
                SET v_res = v_papa_papa;
            END IF;
            SET v_res = CONCAT(v_res,'-',v_papa);
        END LOOP;
        close csr;
        return v_res;
    END
    

    그리고 당신 단지

    select get_lineage(the_id)
    

    그것은 누군가가 도움이되기를 바랍니다 :)

  5. ==============================

    5.여기에 또 다른 quetion에 대해 같은 일을했다

    여기에 또 다른 quetion에 대해 같은 일을했다

    재귀 선택 MYSQL은 여러 수준의 모든 자식을 얻을

    쿼리는 다음과 같습니다

    SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM (
      SELECT @pv:=(
        SELECT GROUP_CONCAT(id SEPARATOR ',')
        FROM table WHERE parent_id IN (@pv)
      ) AS lv FROM table 
      JOIN
      (SELECT @pv:=1)tmp
      WHERE parent_id IN (@pv)
    ) a;
    
  6. ==============================

    6.당신이 빠른 읽기 속도를 필요로하는 경우, 최선의 선택은 폐쇄 테이블을 사용하는 것입니다. 클로저의 표는 각 상위 / 하위 쌍에 대한 행을 포함합니다. 귀하의 예제에서 그래서, 폐쇄 테이블과 같을 것이다

    당신이 빠른 읽기 속도를 필요로하는 경우, 최선의 선택은 폐쇄 테이블을 사용하는 것입니다. 클로저의 표는 각 상위 / 하위 쌍에 대한 행을 포함합니다. 귀하의 예제에서 그래서, 폐쇄 테이블과 같을 것이다

    ancestor | descendant | depth
    0        | 0          | 0
    0        | 19         | 1
    0        | 20         | 2
    0        | 21         | 3
    0        | 22         | 4
    19       | 19         | 0
    19       | 20         | 1
    19       | 21         | 3
    19       | 22         | 4
    20       | 20         | 0
    20       | 21         | 1
    20       | 22         | 2
    21       | 21         | 0
    21       | 22         | 1
    22       | 22         | 0
    

    이 테이블이 있으면, 계층 적 쿼리는 매우 쉽고 빠르게된다. 카테고리 (20)의 모든 자손을 얻으려면 :

    SELECT cat.* FROM categories_closure AS cl
    INNER JOIN categories AS cat ON cat.id = cl.descendant
    WHERE cl.ancestor = 20 AND cl.depth > 0
    

    물론,이 같은 비정규 데이터를 사용할 때마다 큰 단점이있다. 당신은 당신의 카테고리 테이블과 함께 폐쇄 테이블을 유지해야합니다. 가장 좋은 방법은 사용 트리거에 아마이지만, 제대로 폐쇄 테이블에 삽입 / 업데이트 / 삭제를 추적 다소 복잡하다. 아무것도, 당신은 당신의 요구 사항을보고 당신을 위해 무엇이 최선인지 방법을 결정해야합니다.

    편집 : 관계형 데이터베이스에서 계층 적 데이터를 저장하는 옵션은 무엇 질문을 참조하십시오? 더 많은 옵션을합니다. 다른 상황에 대해 서로 다른 최적의 솔루션이 있습니다.

  7. ==============================

    7.목록 아이에게 간단한 쿼리는 첫 번째 재귀입니다 :

    목록 아이에게 간단한 쿼리는 첫 번째 재귀입니다 :

    select @pv:=id as id, name, parent_id
    from products
    join (select @pv:=19)tmp
    where parent_id=@pv
    

    결과:

    id  name        parent_id
    20  category2   19
    21  category3   20
    22  category4   21
    26  category24  22
    

    ... 왼쪽과 조인

    select
        @pv:=p1.id as id
      , p2.name as parent_name
      , p1.name name
      , p1.parent_id
    from products p1
    join (select @pv:=19)tmp
    left join products p2 on p2.id=p1.parent_id -- optional join to get parent name
    where p1.parent_id=@pv
    

    @tincot의 솔루션은 모든 어린이를 나열합니다 :

    select  id,
            name,
            parent_id 
    from    (select * from products
             order by parent_id, id) products_sorted,
            (select @pv := '19') initialisation
    where   find_in_set(parent_id, @pv) > 0
    and     @pv := concat(@pv, ',', id)
    

    온라인는 SQL 바이올린과 그것을 테스트하고 모든 결과를 참조하십시오.

    http://sqlfiddle.com/#!9/a318e3/4/0

  8. ==============================

    8.당신은 재귀 쿼리 (성능에 YMMV)로 아주 쉽게 다른 데이터베이스에서 이런 식으로 할 수 있습니다.

    당신은 재귀 쿼리 (성능에 YMMV)로 아주 쉽게 다른 데이터베이스에서 이런 식으로 할 수 있습니다.

    이를 수행하는 다른 방법은 데이터의 두 개의 여분의 비트, 좌측 및 우측의 값을 저장하는 것이다. 왼쪽과 오른쪽 값은 당신이 표현하고있는 트리 구조의 예약 주문 탐색에서 파생됩니다.

    이 수정 된 예약 주문 트리 순회로 알고 있고 한 번에 모든 부모 값을 얻기 위해 간단한 쿼리를 실행할 수 있습니다. 또한 이름이 "중첩 된 세트"로 간다.

  9. ==============================

    9.그냥 MySQL의에서 자기 관계 테이블의 메이크업 트리에 대한 BlueM / 트리 PHP 클래스를 사용합니다.

    그냥 MySQL의에서 자기 관계 테이블의 메이크업 트리에 대한 BlueM / 트리 PHP 클래스를 사용합니다.

    여기 BlueM / 트리를 사용하는 예는 다음과 같습니다

    <?php 
    require '/path/to/vendor/autoload.php'; $db = new PDO(...); // Set up your database connection 
    $stm = $db->query('SELECT id, parent, title FROM tablename ORDER BY title'); 
    $records = $stm->fetchAll(PDO::FETCH_ASSOC); 
    $tree = new BlueM\Tree($records); 
    ...
    
  10. ==============================

    10.당신을 위해 작동하는지 그것 조금 까다로운 하나,이를 확인

    당신을 위해 작동하는지 그것 조금 까다로운 하나,이를 확인

    select a.id,if(a.parent = 0,@varw:=concat(a.id,','),@varw:=concat(a.id,',',@varw)) as list from (select * from recursivejoin order by if(parent=0,id,parent) asc) a left join recursivejoin b on (a.id = b.parent),(select @varw:='') as c  having list like '%19,%';
    

    SQL 바이올린 링크 http://www.sqlfiddle.com/#!2/e3cdf/2

    적절하게 필드와 테이블 이름으로 교체합니다.

  11. ==============================

    11.

    이 범주 테이블입니다.

    SELECT  id,
            NAME,
            parent_category 
    FROM    (SELECT * FROM category
             ORDER BY parent_category, id) products_sorted,
            (SELECT @pv := '2') initialisation
    WHERE   FIND_IN_SET(parent_category, @pv) > 0
    AND     @pv := CONCAT(@pv, ',', id)
    

    산출::

  12. ==============================

    12.A는 (갱신 삽입, 삭제) 쉽게 항목을 허용 대답하지만 큰 계층 구조 쿼리에 대한 다른 저비용의 두 번째 대안 유사한 비트 및 있지만 뭔가는 각 항목에 대한 지속적인 경로 열을 추가 할, 여기 것이라고 언급되지.

    A는 (갱신 삽입, 삭제) 쉽게 항목을 허용 대답하지만 큰 계층 구조 쿼리에 대한 다른 저비용의 두 번째 대안 유사한 비트 및 있지만 뭔가는 각 항목에 대한 지속적인 경로 열을 추가 할, 여기 것이라고 언급되지.

    일부 같은 :

    id | name        | path
    19 | category1   | /19
    20 | category2   | /19/20
    21 | category3   | /19/20/21
    22 | category4   | /19/20/21/22
    

    예:

    -- get children of category3:
    SELECT * FROM my_table WHERE path LIKE '/19/20/21%'
    -- Reparent an item:
    UPDATE my_table SET path = REPLACE(path, '/19/20', '/15/16') WHERE path LIKE '/19/20/%'
    

    대신 실제 숫자 경로 ID를 인코딩 base36를 사용하여 경로 경로 길이 및 ORDER 최적화

     // base10 => base36
     '1' => '1',
     '10' => 'A',
     '100' => '2S',
     '1000' => 'RS',
     '10000' => '7PS',
     '100000' => '255S',
     '1000000' => 'LFLS',
     '1000000000' => 'GJDGXS',
     '1000000000000' => 'CRE66I9S'
    

    https://en.wikipedia.org/wiki/Base36

    상기 인코딩 된 ID에 고정 길이 패딩을 사용하여도 슬래시 '/'구분자를 억제

    자세한 최적화 설명 여기 : https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/

    ALL

    분할 경로에 함수 또는 프로 시저를 구축하는 것은 하나 개의 항목의 조상을 검색하기위한

  13. ==============================

    13.나를 위해이 작품이 너무 당신을 위해 작동 바랍니다. 그것은 어떤 특정 메뉴 당신에게 아이에 대한 기록 세트 루트를 제공 할 것입니다. 당신의 요구 사항에 따라 필드 이름을 변경합니다.

    나를 위해이 작품이 너무 당신을 위해 작동 바랍니다. 그것은 어떤 특정 메뉴 당신에게 아이에 대한 기록 세트 루트를 제공 할 것입니다. 당신의 요구 사항에 따라 필드 이름을 변경합니다.

    SET @id:= '22';
    
    SELECT Menu_Name, (@id:=Sub_Menu_ID ) as Sub_Menu_ID, Menu_ID 
    FROM 
        ( SELECT Menu_ID, Menu_Name, Sub_Menu_ID 
          FROM menu 
          ORDER BY Sub_Menu_ID DESC
        ) AS aux_table 
        WHERE Menu_ID = @id
         ORDER BY Sub_Menu_ID;
    
  14. ==============================

    14.나는 더 쉽게 발견

    나는 더 쉽게 발견

    1) 항목이 다른 하나의 상위 계층에 어디 있는지 확인하는 함수를 만듭니다. 이런 식으로 뭔가 (내가 기능을 쓰기 DO WHILE으로하지 않습니다)

    is_related(id, parent_id);
    

    귀하의 예제에서

    is_related(21, 19) == 1;
    is_related(20, 19) == 1;
    is_related(21, 18) == 0;
    

    2) 사용이 뭔가를 서브 - 선택 :

    select ...
    from table t
    join table pt on pt.id in (select i.id from table i where is_related(t.id,i.id));
    
  15. ==============================

    15.나는 당신을 위해 쿼리를 만들었습니다. 이렇게하면 단일 쿼리와 재귀 카테고리를 줄 것이다 :

    나는 당신을 위해 쿼리를 만들었습니다. 이렇게하면 단일 쿼리와 재귀 카테고리를 줄 것이다 :

    SELECT id,NAME,'' AS subName,'' AS subsubName,'' AS subsubsubName FROM Table1 WHERE prent is NULL
    UNION 
    SELECT b.id,a.name,b.name AS subName,'' AS subsubName,'' AS subsubsubName FROM Table1 AS a LEFT JOIN Table1 AS b ON b.prent=a.id WHERE a.prent is NULL AND b.name IS NOT NULL 
    UNION 
    SELECT c.id,a.name,b.name AS subName,c.name AS subsubName,'' AS subsubsubName FROM Table1 AS a LEFT JOIN Table1 AS b ON b.prent=a.id LEFT JOIN Table1 AS c ON c.prent=b.id WHERE a.prent is NULL AND c.name IS NOT NULL 
    UNION 
    SELECT d.id,a.name,b.name AS subName,c.name AS subsubName,d.name AS subsubsubName FROM Table1 AS a LEFT JOIN Table1 AS b ON b.prent=a.id LEFT JOIN Table1 AS c ON c.prent=b.id LEFT JOIN Table1 AS d ON d.prent=c.id WHERE a.prent is NULL AND d.name IS NOT NULL 
    ORDER BY NAME,subName,subsubName,subsubsubName
    

    여기에 바이올린입니다.

  16. from https://stackoverflow.com/questions/20215744/how-to-create-a-mysql-hierarchical-recursive-query by cc-by-sa and MIT license