Jeśli używasz MySQL 8.0 lub MariaDB 10.2 (lub wyższy) możesz wypróbować rekurencyjne CTE (typowe wyrażenia tabelowe) .
Zakładając następujący schemat i dane:
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);
Teraz spróbuj wstawić nowy wiersz z child_id = 1
i parent_id = 4
. Ale to stworzyłoby cykliczne relacje (1->4->2->1 i 1->4->3->1 ), którym chcesz zapobiec. Aby dowiedzieć się, czy relacja odwrotna już istnieje, możesz użyć następującego zapytania, które pokaże wszystkich rodziców listy 4 (w tym odziedziczeni/przechodni rodzice):
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
Wynik byłby następujący:
child_id | parent_id
4 | 2
4 | 3
2 | 1
3 | 1
W wyniku widać, że lista 1 jest jednym z rodziców listy 4 i nie wstawilibyście nowego rekordu.
Ponieważ chcesz tylko wiedzieć, czy lista 1 jest w wyniku, możesz zmienić ostatnią linię na
select * from rcte where parent_id = @new_child_id limit 1
lub do
select exists (select * from rcte where parent_id = @new_child_id)
BTW:Możesz użyć tego samego zapytania, aby zapobiec zbędnym relacjom. Zakładając, że chcesz wstawić rekord z child_id = 4
i parent_id = 1
. Byłoby to zbędne, ponieważ lista 4 już dziedziczy listę 1 ponad listą 2 i lista 3 . Następujące zapytanie pokaże Ci, ż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)
Możesz użyć podobnego zapytania, aby uzyskać wszystkie odziedziczone elementy:
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