들어 MySQL은 8 + : 재귀 사용하는 with
구문을.
들어 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
공통 테이블 표현식 (최대 버전 5.7)을 지원하지 않는 MySQL 버전의 경우 다음 쿼리를 사용하여이를 달성 할 수 있습니다.
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
기능은 반드시이 조건이 경우에도, 항상 true인지 확인하기 위해 호출되는 pv
문자열이 어떤 이유에 대한 falsy 값을 얻을 것입니다.
대체로, 이러한 가정은 의지하기에는 너무 위험 할 수 있습니다. 문서는 경고 :
당신은 당신이 기대하는 결과를 얻을 수 있지만 이것은 보장되지 않습니다 [...] 사용자 변수와 관련된 표현식에 대한 평가 순서는 정의되어 있지 않습니다.
따라서 위의 쿼리와 일관되게 작동하더라도 조건을 추가하거나이 쿼리를 더 큰 쿼리에서 뷰 또는 하위 쿼리로 사용하는 경우와 같이 평가 순서가 계속 변경 될 수 있습니다. 향후 MySQL 릴리스에서 제거 될 "기능"입니다 .
이전 MySQL 릴리스에서는 이외의 명령문에서 사용자 변수에 값을 할당 할 수 SET
있었습니다. 이 기능은 이전 버전과의 호환성을 위해 MySQL 8.0에서 지원되지만 향후 MySQL 릴리스에서는 제거 될 예정입니다.
위에서 언급했듯이 MySQL 8.0부터는 재귀를 사용해야합니다. with
구문을 합니다.
능률
매우 큰 데이터 세트의 경우 find_in_set
조작이 목록에서 숫자를 찾는 가장 이상적인 방법이 아니므로 리턴 된 레코드 수와 동일한 크기의 크기에 도달하는 목록이 아닌 경우이 솔루션이 느려질 수 있습니다 .
대안 1 : with recursive
,connect by
점점 더 많은 데이터베이스 가 재귀 쿼리에 대해 SQL : 1999 ISO 표준 WITH [RECURSIVE]
구문 을 구현 합니다 (예 : Postgres 8.4+ , SQL Server 2005+ , DB2 , Oracle 11gR2 + , SQLite 3.8.4+ , Firebird 2.1+ , H2 , HyperSQL 2.1.0+ , Teradata , MariaDB 10.2.2+ ). 그리고 버전 8.0부터 MySQL도 지원합니다 . 사용할 구문은이 답변의 상단을 참조하십시오.
일부 데이터베이스에는 Oracle , DB2 , Informix , CUBRID 및 기타 데이터베이스에서 CONNECT BY
사용 가능한 절 과 같은 계층 적 조회를위한 비표준 대체 구문이 있습니다.
MySQL 버전 5.7은 이러한 기능을 제공하지 않습니다. 데이터베이스 엔진이이 구문을 제공하거나 제공하는 구문으로 마이그레이션 할 수 있으면 이것이 가장 적합한 옵션 일 것입니다. 그렇지 않은 경우 다음 대안도 고려하십시오.
대안 2 : 경로 스타일 식별자
id
계층 적 정보를 포함하는 값인 경로를 할당하면 상황이 훨씬 쉬워집니다 . 예를 들어 귀하의 경우 다음과 같이 보일 수 있습니다.
ID | NAME
19 | category1
19/1 | category2
19/1/1 | category3
19/1/1/1 | category4
그렇다면 당신의 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
하는 부모 조건을 지정 당신의 자손을 검색합니다. 필요에 따라 더 많은 레벨로이 쿼리를 확장 할 수 있습니다.