sql >> Databáze >  >> RDS >> Mysql

Prevence kruhového spojování, rekurzivní vyhledávání

Pokud používáte MySQL 8.0 nebo MariaDB 10.2 (nebo vyšší) můžete vyzkoušet rekurzivní CTE (běžné tabulkové výrazy) .

Za předpokladu následujícího schématu a dat:

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

Nyní zkuste vložit nový řádek s child_id = 1 a parent_id = 4 . To by ale vytvořilo cyklické vztahy (1->4->2->1 a 1->4->3->1 ), kterému chcete zabránit. Chcete-li zjistit, zda již existuje obrácený vztah, můžete použít následující dotaz, který zobrazí všechny rodiče seznamu 4 (včetně zděděných/přechodných rodičů):

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

Výsledek by byl:

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

Ukázka

Ve výsledku můžete vidět, že seznam 1 je jedním z rodičů seznamu 4 a nevložili byste nový záznam.

Protože chcete vědět, zda seznam 1 je ve výsledku, můžete změnit poslední řádek na

select * from rcte where parent_id = @new_child_id limit 1

nebo do

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

BTW:Stejný dotaz můžete použít k zamezení redundantních vztahů. Za předpokladu, že chcete vložit záznam s child_id = 4 a parent_id = 1 . To by bylo nadbytečné, protože seznam 4 již zdědí seznam 1 nad seznamem 2 a seznam 3 . Následující dotaz vám ukáže, ž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)

A můžete použít podobný dotaz k získání všech zděděných položek:

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. Existuje nějaký bezpečný způsob, jak parametrizovat názvy databází v dotazech MySQL?

  2. Proč potřebujete datové modelování?

  3. Převeďte sadu výsledků z pole SQL na pole řetězců

  4. Příliš mnoho stolů; MySQL může ve spojení používat pouze 61 tabulek