Pokud používáte MySQL 8.0 nebo MariaDB 10.2 (nebo vyšší) můžete vyzkoušet rekurzivní CTE (běžné tabulkové výrazy) .
Za předpokladu následujícího schématu a dat:
CREATE TABLE `list_relation` (
`child_id` int unsigned NOT NULL,
`parent_id` int unsigned NOT NULL,
PRIMARY KEY (`child_id`,`parent_id`)
);
insert into list_relation (child_id, parent_id) values
(2,1),
(3,1),
(4,2),
(4,3),
(5,3);
Nyní zkuste vložit nový řádek s child_id = 1
a parent_id = 4
. To by ale vytvořilo cyklické vztahy (1->4->2->1 a 1->4->3->1 ), kterému chcete zabránit. Chcete-li zjistit, zda již existuje obrácený vztah, můžete použít následující dotaz, který zobrazí všechny rodiče seznamu 4 (včetně zděděných/přechodných rodičů):
set @new_child_id = 1;
set @new_parent_id = 4;
with recursive rcte as (
select *
from list_relation r
where r.child_id = @new_parent_id
union all
select r.*
from rcte
join list_relation r on r.child_id = rcte.parent_id
)
select * from rcte
Výsledek by byl:
child_id | parent_id
4 | 2
4 | 3
2 | 1
3 | 1
Ve výsledku můžete vidět, že seznam 1 je jedním z rodičů seznamu 4 a nevložili byste nový záznam.
Protože chcete vědět, zda seznam 1 je ve výsledku, můžete změnit poslední řádek na
select * from rcte where parent_id = @new_child_id limit 1
nebo do
select exists (select * from rcte where parent_id = @new_child_id)
BTW:Stejný dotaz můžete použít k zamezení redundantních vztahů. Za předpokladu, že chcete vložit záznam s child_id = 4
a parent_id = 1
. To by bylo nadbytečné, protože seznam 4 již zdědí seznam 1 nad seznamem 2 a seznam 3 . Následující dotaz vám ukáže, že:
set @new_child_id = 4;
set @new_parent_id = 1;
with recursive rcte as (
select *
from list_relation r
where r.child_id = @new_child_id
union all
select r.*
from rcte
join list_relation r on r.child_id = rcte.parent_id
)
select exists (select * from rcte where parent_id = @new_parent_id)
A můžete použít podobný dotaz k získání všech zděděných položek:
set @list = 4;
with recursive rcte (list_id) as (
select @list
union distinct
select r.parent_id
from rcte
join list_relation r on r.child_id = rcte.list_id
)
select distinct i.*
from rcte
join item i on i.list_id = rcte.list_id