source

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

bestscript 2022. 11. 24. 23:42

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

MySQL 테이블은 다음과 같습니다.

아이디 이름. parent_id
19 카테고리 1 0
20 카테고리 2 19
21 카테고리 3 20
22 카테고리 4 21
... ... ...

하여 ID(예를 들어 "MySQL" "ID" "D" "MySQL" "ID" "MySQL" "MySQL" "ID" "MySQL" "MySQL" "MySQL" "MySQL" "ID" "MySQL" "MySQL" (" "MySQL "MySQL")id=19모든 아이디를 가져옵니다.[20, 21, 22]...

아이들의 계급은 알려지지 않았다.그것은 다를 수 있다...

는 그것을 .for......쿼리를 한 기능을 구현하려면 어떻게 해야 합니까?단일 MySQL 쿼리를 사용하여 동일한 기능을 구현하려면 어떻게 해야 합니까?

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

MySQL 8+

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.x

Common Table Expressions를 지원하지 않는 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 < id그렇지 않으면 결과가 완전하지 않습니다.

쿼리 내의 변수 할당

이 쿼리는 특정 MySQL 구문을 사용합니다. 변수는 실행 중에 할당 및 수정됩니다.실행 순서에는 다음과 같은 전제 조건이 있습니다.

  • from절이 먼저 평가됩니다. 서부터가 되겠네요@pv기화됩됩니니다
  • where되며, .from(기본 의 모든 점차적으로 에 추가됩니다).@pv를 참조해 주세요.
  • ''의 where절은 순서대로 평가되며 전체 결과가 확실하면 평가가 중단됩니다. 때문에 두 .것음id로 이행합니다.은, 「부모 리스트」가 있는 합니다.이 작업은, 다음의 경우에 한정해 행해집니다.id첫 번째 조건을 통과합니다.length는 이 이 항상 됩니다.pv문자열은 어떤 이유로든 잘못된 값을 생성합니다.

대체로, 이러한 가정은 신뢰하기에는 너무 위험하다고 생각할 수 있습니다. 문서에서는 다음과 같이 경고하고 있습니다.

원하는 결과를 얻을 수 있지만 사용자 변수와 관련된 식의 평가 순서가 정의되지 않았습니다.

따라서 위의 쿼리와 일관되게 작동하더라도 조건을 추가하거나 더 큰 쿼리에서 이 쿼리를 뷰 또는 하위 쿼리로 사용하는 경우 등 평가 순서가 변경될 수 있습니다.는 향후 MySQL 릴리즈에서 제거될 "기능"입니다.

의 이전 에서는 MySQL 에서 사용자 할 수 .SET 8되지만 MySQL의

설명한 8.0 MySQL 8.0을 .with구문을 사용합니다.

효율성.

데이터 세트가 매우 크면 이 솔루션이 느려질 수 있습니다.이것은, 리스트내의 숫자를 찾는 가장 이상적인 방법이 아니기 때문입니다.반환되는 레코드수와 같은 크기의 리스트가 아닌 것은 확실합니다.

1: 대 1:with recursive,connect by

SQL:1999 ISO 표준 구문을 재귀 쿼리에 구현하는 데이터베이스가 증가하고 있습니다(예: Postgres 8.4+, SQL Server 2005+, DB2, Oracle 11gR2+, SQLite 3.8.4+, Firebird 2.1+, H2, Hyper).SQL 2.1.0+, Teradata, MariaDB 10.2.2+).버전 8.0부터는 MySQL도 지원합니다.사용하는 구문에 대해서는, 이 회답의 상단을 참조해 주세요.

일부 에는 계층형.를 들어 다음과 같습니다.CONNECT BY절은 Oracle, DB2, Informix, CUBRID 및 기타 데이터베이스에서 사용할 수 있습니다.

MySQL 버전 5.7은 이러한 기능을 제공하지 않습니다.데이터베이스 엔진에서 이 구문을 제공하거나 이 구문을 제공하는 구문으로 마이그레이션할 수 있는 경우에는 이 구문을 사용하는 것이 가장 좋습니다.그렇지 않은 경우 다음 대안도 고려하십시오.

대체 2: 경로 스타일 식별자

배정을 .id층층정정정보 : 경경경경 。예를 들어, 다음과 같은 경우가 있습니다.

아이디 이름.
19 카테고리 1
19/1 카테고리 2
19/1/1 카테고리 3
19/1/1/1 카테고리 4

당신의 ★★★★★★★★★★★★★★★★★.select음음음같 뭇매하다

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

대안 3: 반복적인 셀프 조인

인 계층 트리를 할 수 .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은 .condition의 항목을 합니다.필요에 따라 더 많은 수준으로 이 쿼리를 확장할 수 있습니다.

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 쿼리를 실행하는 방법은 무엇입니까?

다음을 시험해 보십시오.

테이블 정의:

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 |
+----+-----------+-----------------------------------------+

여기서 또 같은 짓을 했군

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;

빠른 읽기 속도가 필요한 경우 닫힘 테이블을 사용하는 것이 가장 좋습니다.폐쇄 테이블에는 각 상위/후속 쌍에 대한 행이 포함됩니다.이 예에서 폐색 테이블은 다음과 같습니다.

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

물론 이와 같이 정규화 해제된 데이터를 사용할 때마다 큰 단점이 있습니다.카테고리 테이블과 함께 닫힘 테이블을 유지해야 합니다.가장 좋은 방법은 트리거를 사용하는 것이지만 폐쇄 테이블의 삽입/업데이트/삭제를 올바르게 추적하는 것은 다소 복잡합니다.다른 모든 것과 마찬가지로 요건을 확인하여 자신에게 가장 적합한 접근방식을 결정해야 합니다.

편집: 관계형 데이터베이스에 계층 데이터를 저장하는 옵션은 무엇입니까?라는 질문을 참조하십시오.를 참조하십시오.상황에 따라 최적의 솔루션이 다릅니다.

제가 생각해낸 최고의 접근법은

  1. 계보를 사용하여 트리를 저장 및 추적합니다.이 정도면 충분합니다.다른 어떤 접근법보다 수천 배 빠르게 읽을 수 있습니다.또한 DB가 변경되어도 해당 패턴을 유지할 수 있습니다(임의의 DB는 해당 패턴을 사용할 수 있습니다).
  2. 특정 ID에 대한 계보를 결정하는 기능을 사용합니다.
  3. 원하는 대로 사용할 수 있습니다(선택 시, CUD 작업 시, 작업 시).

Lineage access descr.는 여기 또는 여기 등 어디에서나 찾을 수 있습니다.기능에 관해서는 - 그것이 나를 흥분시킨 것이다.

결국 어느 정도 심플하고 비교적 빠르고 심플한 솔루션을 얻을 수 있었습니다.

함수의 본문

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

누군가에게 도움이 되길 바랍니다:)

@는 @trincot, @trincot을 합니다.WITH RECURSIVE ()를 사용하여 브레드 크럼을 작성하기 위한 스테이트먼트id현재 페이지의 계층에서 뒤로 이동해서 모든 데이터를 찾을 수 있습니다.parent 집에서는routetable.syslog를 클릭합니다.

그래서 이 @trincot 솔루션은 후손이 아닌 부모를 찾기 위해 반대 방향으로 변형되어 있습니다.

저도 덧붙였습니다.depth결과 순서를 반전하는 데 유용한 값입니다(단, 브레드 크럼이 거꾸로 되어 있을 수 있습니다).

WITH RECURSIVE cte (
    `id`,
    `title`,
    `url`,
    `icon`,
    `class`,
    `parent_id`,
    `depth`
) AS (
    SELECT   
        `id`,
        `title`,
        `url`,
        `icon`,
        `class`,
        `parent_id`,
        1 AS `depth` 
    FROM     `route`
    WHERE    `id` = :id
      
    UNION ALL 
    SELECT 
        P.`id`,
        P.`title`,
        P.`url`,
        P.`icon`,
        P.`class`,
        P.`parent_id`,
        `depth` + 1
    FROM `route` P
        
    INNER JOIN cte
        ON P.`id` = cte.`parent_id`
)
SELECT * FROM cte ORDER BY `depth` DESC;

mySQL 8+로 업그레이드하기 전에 vars를 사용했지만 권장되지 않아 8.0.22 버전에서는 더 이상 작동하지 않습니다!

EDIT 2021-02-19 : 계층 메뉴 예시

@david 코멘트 후 모든 노드가 포함된 완전한 계층 메뉴를 만들기로 결심하고 원하는 대로 정렬했습니다.sorting각각깊서서항항항항항렬열열열열열열각 。사용자/인증 매트릭스 페이지에 매우 유용합니다.

따라서 각 깊이(PHP 루프)에 대해 1개의 쿼리로 이전 버전을 단순화할 수 있습니다.

ERP 인증 매트릭스

예에서는, 를 「」, 「INSER JOIN」과 .url웹 사이트(멀티 웹 사이트 CMS 시스템)별로 경로를 필터링하는 테이블입니다.

본질적인 것을 볼 수 .path 포함하는 칸CONCAT()메뉴를 올바른 방법으로 정렬하는 기능을 제공합니다.

SELECT R.* FROM (
    WITH RECURSIVE cte (
        `id`,
        `title`,
        `url`,
        `icon`,
        `class`,
        `parent`,
        `depth`,
        `sorting`,
        `path`
    ) AS (
        SELECT 
            `id`,
            `title`,
            `url`,
            `icon`,
            `class`,
            `parent`,
            1 AS `depth`,
            `sorting`,
            CONCAT(`sorting`, ' ' , `title`) AS `path`
        FROM `route`
        WHERE `parent` = 0
        UNION ALL SELECT 
            D.`id`,
            D.`title`,
            D.`url`,
            D.`icon`,
            D.`class`,
            D.`parent`,
            `depth` + 1,
            D.`sorting`,
            CONCAT(cte.`path`, ' > ', D.`sorting`, ' ' , D.`title`)
        FROM `route` D
        INNER JOIN cte
            ON cte.`id` = D.`parent`
    )
    SELECT * FROM cte
) R

INNER JOIN `url` U
    ON R.`id` = U.`route_id`
    AND U.`site_id` = 1

ORDER BY `path` ASC  

첫 번째 재귀의 하위 항목을 나열하는 간단한 쿼리:

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 Fidle을 사용하여 온라인으로 테스트하고 모든 결과를 확인합니다.

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

여기에 기재되어 있지 않은 것은 인정된 답변의 두 번째 대안과 약간 비슷하지만 큰 계층 쿼리와 쉬운(업데이트 삭제 삽입) 항목에 대해 다르고 비용도 저렴하지만 각 항목에 대해 영속적인 경로 열을 추가하는 것입니다.

다음과 같은 것이 있습니다.

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/%'

와 ""를 합니다.ORDER BY path 경로 사용

 // 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/ 를 참조해 주세요.

하기 위해서

한 항목의 조상을 찾기 위해 경로를 분할하는 기능 또는 절차를 구축하는 것

재귀 쿼리(성능에 대한 YMMV)를 사용하면 다른 데이터베이스에서도 이와 같이 쉽게 수행할 수 있습니다.

또 다른 방법은 두 개의 추가 데이터 비트(왼쪽과 오른쪽 값)를 저장하는 것입니다.왼쪽 및 오른쪽 값은 나타내는 트리 구조의 사전 순서 트래버스에서 파생됩니다.

이를 수정 사전 순서 트리 트래버설이라고 하며 간단한 쿼리를 실행하여 모든 부모 값을 한 번에 가져올 수 있습니다.또한 "nasted set"이라는 이름으로 통합니다.

mysql에 있는 자기 관계 테이블의 트리를 만들기 위해 BlueM/tree php 클래스를 사용합니다.

Tree 및 Tree\Node는 부모 ID 참조를 사용하여 계층적으로 구조화된 데이터를 처리하기 위한 PHP 클래스입니다.일반적인 예로는 관계형 데이터베이스의 테이블에서 각 레코드의 "상위" 필드가 다른 레코드의 기본 키를 참조합니다.물론 Tree는 데이터베이스에서 생성된 데이터만 사용할 수 있는 것이 아니라 사용자가 데이터를 제공하고 Tree는 데이터의 출처와 처리 방법에 관계없이 데이터를 사용합니다.더 읽다

다음으로 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); 
...

여기에 이미지 설명 입력

카테고리 테이블입니다.

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)

출력: 여기에 이미지 설명 입력

조금 까다로운 문제지만, 이 방법이 당신에게 적합한지 확인해 보세요.

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

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

이건 나한테도 효과가 있어, 너도 효과가 있길 바라.그러면 특정 메뉴에 대해 Root to Child 레코드 세트가 제공됩니다.필요에 따라 필드 이름을 변경합니다.

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;

보다 쉽게 찾을 수 있었습니다.

1) 항목이 다른 항목의 상위 계층에 있는지 확인하는 기능을 만듭니다.다음과 같은 경우(함수는 쓰지 않고 WHIT DO로 만듭니다):

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

당신을 위해 질문을 했습니다.이렇게 하면 단일 쿼리가 있는 재귀 카테고리가 제공됩니다.

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

여기 바이올린이 있습니다.

언급URL : https://stackoverflow.com/questions/20215744/how-to-create-a-mysql-hierarchical-recursive-query