Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Zapobieganie łączeniu kołowemu, wyszukiwaniu rekurencyjnemu

Jeśli używasz MySQL 8.0 lub MariaDB 10.2 (lub wyższy) możesz wypróbować rekurencyjne CTE (typowe wyrażenia tabelowe) .

Zakładając następujący schemat i dane:

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);

Teraz spróbuj wstawić nowy wiersz z child_id = 1 i parent_id = 4 . Ale to stworzyłoby cykliczne relacje (1->4->2->1 i 1->4->3->1 ), którym chcesz zapobiec. Aby dowiedzieć się, czy relacja odwrotna już istnieje, możesz użyć następującego zapytania, które pokaże wszystkich rodziców listy 4 (w tym odziedziczeni/przechodni rodzice):

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

Wynik byłby następujący:

child_id | parent_id
       4 |         2
       4 |         3
       2 |         1
       3 |         1

Demo

W wyniku widać, że lista 1 jest jednym z rodziców listy 4 i nie wstawilibyście nowego rekordu.

Ponieważ chcesz tylko wiedzieć, czy lista 1 jest w wyniku, możesz zmienić ostatnią linię na

select * from rcte where parent_id = @new_child_id limit 1

lub do

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

BTW:Możesz użyć tego samego zapytania, aby zapobiec zbędnym relacjom. Zakładając, że chcesz wstawić rekord z child_id = 4 i parent_id = 1 . Byłoby to zbędne, ponieważ lista 4 już dziedziczy listę 1 ponad listą 2 i lista 3 . Następujące zapytanie pokaże Ci, że:

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)

Możesz użyć podobnego zapytania, aby uzyskać wszystkie odziedziczone elementy:

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. Co się stanie, jeśli połączenia MySQL nie będą ciągle zamykane na stronach PHP?

  2. Transakcje MySQL w ramach transakcji

  3. Samouczek dotyczący tworzenia kopii zapasowych i przywracania (eksportowania i importowania) baz danych MySQL

  4. Konwertuj wyniki SQL na tablicę PHP

  5. Definiowanie klucza złożonego z automatycznym przyrostem w MySQL