Mysql
 sql >> база данни >  >> RDS >> Mysql

Предотвратяване на кръгово свързване, рекурсивни търсения

Ако използвате MySQL 8.0 или MariaDB 10.2 (или по-високо) можете да опитате рекурсивни CTEs (изрази за обща таблица) .

Приемайки следната схема и данни:

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 е в резултата, можете да промените последния ред на

select * from rcte where parent_id = @new_child_id limit 1

или до

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

BTW:Можете да използвате същата заявка, за да предотвратите излишни връзки. Ако приемем, че искате да вмъкнете записа с 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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да извлечете качени файлове с помощта на php

  2. Mysql 5.6 главоболия на Mac OSX

  3. QMYSQL драйверът не е зареден в Windows

  4. Фатална грешка:Не може да се използва обект от тип stdClass като масив в

  5. JSON_ARRAY_APPEND() – Добавяне на стойности към JSON масив в MySQL