Nếu bạn sử dụng MySQL 8.0 hoặc MariaDB 10.2 (hoặc cao hơn) bạn có thể thử CTE đệ quy (biểu thức bảng phổ biến) .
Giả sử lược đồ và dữ liệu sau:
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);
Bây giờ bạn thử chèn một hàng mới với child_id = 1
và parent_id = 4
. Nhưng điều đó sẽ tạo ra các quan hệ tuần hoàn ( 1-> 4-> 2-> 1 và 1-> 4-> 3-> 1 ), mà bạn muốn ngăn chặn. Để tìm hiểu xem mối quan hệ ngược đã tồn tại chưa, bạn có thể sử dụng truy vấn sau, truy vấn này sẽ hiển thị tất cả các bậc cha mẹ của danh sách 4 (bao gồm cả cha mẹ thừa kế / lai):
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
Kết quả sẽ là:
child_id | parent_id
4 | 2
4 | 3
2 | 1
3 | 1
Bạn có thể thấy trong kết quả, danh sách 1 là một trong những bậc cha mẹ của danh sách 4 và bạn sẽ không chèn bản ghi mới.
Vì bạn chỉ muốn biết nếu danh sách 1 là kết quả, bạn có thể thay đổi dòng cuối cùng thành
select * from rcte where parent_id = @new_child_id limit 1
hoặc để
select exists (select * from rcte where parent_id = @new_child_id)
BTW:Bạn có thể sử dụng cùng một truy vấn để ngăn các quan hệ thừa. Giả sử bạn muốn chèn bản ghi với child_id = 4
và parent_id = 1
. Điều này sẽ là thừa, vì danh sách 4 đã kế thừa danh sách 1 qua danh sách 2 và danh sách 3 . Truy vấn sau sẽ cho bạn thấy rằng:
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)
Và bạn có thể sử dụng một truy vấn tương tự để nhận tất cả các mục được kế thừa:
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