防止循环连接、递归搜索 [英] Preventing circular joining, recursive searches

查看:47
本文介绍了防止循环连接、递归搜索的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以在我的情况下,我有三个表:listitemlist_relation.

So in my situation I have three tables: list, item and list_relation.

每个item将通过list_id外键链接到一个列表.

Each item will be linked to a list through the list_id foreign key.

list_relation 看起来像这样:

CREATE TABLE list_relation
    (
        parent_id INT UNSIGNED NOT NULL,
        child_id INT UNSIGNED NOT NULL,

        UNIQUE(parent_id, child_id)

        FOREIGN KEY (parent_id)
            REFERENCES list (id)
                ON DELETE CASCADE,    

        FOREIGN KEY (child_id)
            REFERENCES list (id)
                ON DELETE CASCADE
    );

我也希望能够从多个列表(包括相关项)继承.

I want to be be able to inherit from multiple lists as well (which includes the related items).

例如我有列表:1、2、3.

For example I have list: 1, 2, 3.

我想知道是否有任何 SQL 方法可以防止出现循环关系.例如

I was wondering if there was any SQL way to prevent there from being a circular relation. E.g.

列表 1 继承自列表 3,列表 2 继承自列表 1,列表 3 继承自列表 1.

List 1 inherits from List 3, List 2 inherits from List 1, List 3 inherits from List 1.

<代码>1 ->2 ->3 ->1

我目前的想法是,我必须先验证所需的继承,然后将其插入数据库,以确定它是否是循环的.

My current idea is that I would have to find out whether it would be circular by validating the desired inheritance first then inserting it into the DB.

推荐答案

如果您使用 MySQL 8.0MariaDB 10.2(或更高版本),您可以尝试 recursiveCTE(常用表表达式).

If you use MySQL 8.0 or MariaDB 10.2 (or higher) you can try recursive CTEs (common table expressions).

假设有以下架构和数据:

Assuming the following schema and data:

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 = 1parent_id = 4 插入一个新行.但这会创建循环关系(1->4->2->11->4->3->1),您想防止这种关系.要确定反向关系是否已经存在,您可以使用以下查询,该查询将显示列表 4 的所有父级(包括继承/传递父级):

Now you try to insert a new row with child_id = 1 and parent_id = 4. But that would create cyclic relations (1->4->2->1 and 1->4->3->1), which you want to prevent. To find out if a reverse relation already exists, you can use the following query, which will show all parents of list 4 (including inherited/transitive parents):

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 的父项之一,您不会插入新记录.

You can see in the result, that the list 1 is one of the parents of list 4, and you wouldn't insert the new record.

既然你只想知道结果中是否有list 1,你可以把最后一行改成

Since you only want to know if list 1 is in the result, you can change the last line to

select * from rcte where parent_id = @new_child_id limit 1

或到

select exists (select * from rcte where parent_id = @new_child_id)

顺便说一句:您可以使用相同的查询来防止冗余关系.假设您要插入带有 child_id = 4parent_id = 1 的记录.这将是多余的,因为 list 4 已经继承了 list 1 而不是 list 2list 3.以下查询将显示:

BTW: You can use the same query to prevent redundant relations. Assuming you want to insert the record with child_id = 4 and parent_id = 1. This would be redundant, since list 4 already inherits list 1 over list 2 and list 3. The following query would show you that:

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)

并且您可以使用类似的查询来获取所有继承的项目:

And you can use a similar query to get all inherited items:

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

这篇关于防止循环连接、递归搜索的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆