防止循环加入,递归搜索

A. *_*Lau 8 mysql recursion

所以在我的情况我有三个表:list,itemlist_relation.

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

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
    );
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

1 -> 2 -> 3 -> 1

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

Pau*_*gel 6

如果您使用MySQL 8.0MariaDB 10.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);
Run Code Online (Sandbox Code Playgroud)

现在,您尝试使用child_id = 1和插入新行parent_id = 4.但这会产生循环关系(1-> 4-> 2-> 11-> 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
Run Code Online (Sandbox Code Playgroud)

结果将是:

child_id | parent_id
       4 |         2
       4 |         3
       2 |         1
       3 |         1
Run Code Online (Sandbox Code Playgroud)

演示

您可以在结果中看到列表1列表4的父项之一,并且您不会插入新记录.

由于您只想知道列表1是否在结果中,因此您可以将最后一行更改为

select * from rcte where parent_id = @new_child_id limit 1
Run Code Online (Sandbox Code Playgroud)

或者

select exists (select * from rcte where parent_id = @new_child_id)
Run Code Online (Sandbox Code Playgroud)

顺便说一句:您可以使用相同的查询来防止冗余关系.假设你想用child_id = 4和插入记录parent_id = 1.这将是多余的,因为列表4已经在列表2列表3上继承了列表1.以下查询将向您显示:

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)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)


小智 1

parent_id在列表中放置一列不是更好吗?

LEFT JOIN然后,您可以通过在列表表上查询 来获取列表树,将parent_id与 匹配list_id,例如:

SELECT t1.list_id, t2.list_id, t3.list_id
FROM list AS t1
LEFT JOIN list as t2 ON t2.parent_id = t1.list_id
LEFT JOIN list as t3 ON t3.parent_id = t2.list_id
WHERE t1.list_id = #your_list_id#
Run Code Online (Sandbox Code Playgroud)

这是您的情况的解决方案吗?不管怎样,我建议你阅读有关在mysql中管理分层数据的内容,你可以找到很多关于这个问题的内容!