MySQL 8.0を使用する場合 またはMariaDB10.2 (またはそれ以上)再帰CTE(一般的なテーブル式)を試すことができます 。
次のスキーマとデータを想定しています:
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);
次に、child_id = 1
で新しい行を挿入しようとします およびparent_id = 4
。しかし、それは循環関係を作成します( 1-> 4-> 2-> 1 および1->4-> 3-> 1 )、これを防ぎたい。逆の関係がすでに存在するかどうかを確認するには、次のクエリを使用できます。これにより、リスト4のすべての親が表示されます。 (継承/推移的な親を含む):
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
結果は次のようになります:
child_id | parent_id
4 | 2
4 | 3
2 | 1
3 | 1
結果から、リスト1であることがわかります。 リスト4の親の1つです 、新しいレコードを挿入しません。
リスト1かどうかだけを知りたいので 結果に、最後の行を
に変更できます。select * from rcte where parent_id = @new_child_id limit 1
またはに
select exists (select * from rcte where parent_id = @new_child_id)
ところで:同じクエリを使用して、冗長な関係を防ぐことができます。child_id = 4
のレコードを挿入するとします。 およびparent_id = 1
。 リスト4なので、これは冗長になります すでにリスト1を継承しています リスト2 およびリスト3 。次のクエリはそれを示します:
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)
また、同様のクエリを使用して、継承されたすべてのアイテムを取得できます。
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