За MySQL 8+: използвайте рекурсивния with
синтаксис.
За MySQL 5.x: използвайте вградени променливи, идентификатори на пътеки или самообединявания.
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
За версиите на 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 синтаксис:променливите се присвояват и променят по време на нейното изпълнение. Направени са някои предположения относно реда на изпълнение:
от
клаузата се оценява първо. И така, това е мястото, където@pv
се инициализира.къде
Клаузата се оценява за всеки запис в реда на извличане отот
псевдоними. Така че тук е поставено условие да включва само записи, за които родителят вече е идентифициран като в дървото на потомците (всички потомци на основния родител постепенно се добавят към@pv
).- Условията в това
къде
клаузите се оценяват по ред и оценката се прекъсва, след като общият резултат е сигурен. Следователно второто условие трябва да бъде на второ място, тъй като добавяid
към родителския списък и това трябва да се случи само акоid
преминава първото условие.дължината
функцията се извиква само, за да се увери, че това условие винаги е вярно, дори акоpv
низ по някаква причина ще даде фалшива стойност.
Като цяло тези предположения могат да се намерят за твърде рискови, за да се разчита. документацията предупреждава:
може да получите резултатите, които очаквате, но това не е гарантирано [...] редът на оценка за изрази, включващи потребителски променливи, е недефиниран.
Така че, въпреки че работи последователно с горната заявка, редът за оценка все още може да се промени, например, когато добавите условия или използвате тази заявка като изглед или подзаявка в по-голяма заявка. Това е „функция“, която ще бъде премахната в бъдеще Издание на MySQL :
Предишните версии на MySQL направиха възможно присвояването на стойност на потребителска променлива в изрази, различни от
SET
. Тази функционалност се поддържа в MySQL 8.0 за обратна съвместимост, но подлежи на премахване в бъдеща версия на MySQL.
Както беше посочено по-горе, от MySQL 8.0 нататък трябва да използвате рекурсивния with
синтаксис.
Ефективност
За много големи набори от данни това решение може да стане бавно, тъй като намери_в_набор
операцията не е най-идеалният начин за намиране на число в списък, със сигурност не в списък, който достига размер от същия порядък като броя на върнатите записи.
Алтернатива 1:с рекурсивно
, свързване чрез
Все повече и повече бази данни прилагат 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 също я поддържа
. Вижте горната част на този отговор за синтаксиса, който да използвате.
Някои бази данни имат алтернативен, нестандартен синтаксис за йерархично търсене, като CONNECT BY
клауза, налична в Oracle
, DB2
, Informix , CUBRID
и други бази данни.
MySQL версия 5.7 не предлага такава функция. Когато вашата база данни предоставя този синтаксис или можете да мигрирате към такъв, който го прави, тогава това със сигурност е най-добрият вариант. Ако не, тогава помислете и за следните алтернативи.
Алтернатива 2:Идентификатори в стил на пътя
Нещата стават много по-лесни, ако зададете id
стойности, които съдържат йерархичната информация:път. Например във вашия случай това може да изглежда така:
ID | NAME |
---|---|
19 | категория1 |
19/1 | категория 2 |
19/1/1 | категория3 |
19/1/1/1 | категория 4 |
След това изберете
ще изглежда така:
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;
Вижте тази цигулка
къде
условието посочва кой родител искате да извлечете потомците. Можете да разширите тази заявка с повече нива, ако е необходимо.