Jika Anda menggunakan MySQL 8.0 atau MariaDB 10.2 (atau lebih tinggi) Anda dapat mencoba CTE rekursif (ekspresi tabel umum) .
Dengan asumsi skema dan data berikut:
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);
Sekarang Anda mencoba memasukkan baris baru dengan child_id = 1
dan parent_id = 4
. Tapi itu akan menciptakan hubungan siklik (1->4->2->1 dan 1->4->3->1 ), yang ingin Anda cegah. Untuk mengetahui apakah relasi terbalik sudah ada, Anda dapat menggunakan kueri berikut, yang akan menampilkan semua induk dari daftar 4 (termasuk orang tua yang diwariskan/transitif):
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
Hasilnya adalah:
child_id | parent_id
4 | 2
4 | 3
2 | 1
3 | 1
Anda dapat melihat pada hasilnya, bahwa daftar 1 adalah salah satu orang tua dari daftar 4 , dan Anda tidak akan memasukkan catatan baru.
Karena Anda hanya ingin tahu apakah daftar 1 ada di hasilnya, Anda dapat mengubah baris terakhir menjadi
select * from rcte where parent_id = @new_child_id limit 1
atau ke
select exists (select * from rcte where parent_id = @new_child_id)
BTW:Anda dapat menggunakan kueri yang sama untuk mencegah hubungan yang berlebihan. Dengan asumsi Anda ingin memasukkan catatan dengan child_id = 4
dan parent_id = 1
. Ini akan berlebihan, karena daftar 4 sudah mewarisi daftar 1 lebih dari daftar 2 dan daftar 3 . Kueri berikut akan menunjukkan kepada Anda bahwa:
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)
Dan Anda dapat menggunakan kueri serupa untuk mendapatkan semua item yang diwarisi:
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