sql >> Database teknologi >  >> RDS >> Mysql

Forhindrer cirkulær sammenføjning, rekursive søgninger

Hvis du bruger MySQL 8.0 eller MariaDB 10.2 (eller højere) kan du prøve rekursive CTE'er (almindelige tabeludtryk) .

Forudsat følgende skema og 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);

Nu prøver du at indsætte en ny række med child_id = 1 og parent_id = 4 . Men det ville skabe cykliske relationer (1->4->2->1 og 1->4->3->1 ), som du vil forhindre. For at finde ud af, om der allerede eksisterer en omvendt relation, kan du bruge følgende forespørgsel, som viser alle forældre til liste 4 (inklusive arvede/transitive forældre):

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

Resultatet ville være:

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

Demo

Du kan se i resultatet, at listen 1 er en af ​​forældrene til liste 4 , og du ville ikke indsætte den nye post.

Da du kun vil vide, om liste 1 er i resultatet, kan du ændre den sidste linje til

select * from rcte where parent_id = @new_child_id limit 1

eller til

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

BTW:Du kan bruge den samme forespørgsel til at forhindre redundante relationer. Forudsat at du vil indsætte posten med child_id = 4 og parent_id = 1 . Dette ville være overflødigt, eftersom liste 4 arver allerede liste 1 over liste 2 og liste 3 . Følgende forespørgsel ville vise dig, at:

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)

Og du kan bruge en lignende forespørgsel til at få alle nedarvede elementer:

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. Erklæring og initialisering af variabler på samme linje i VBA

  2. Java.lang.IllegalStateException:Allerede vedhæftet

  3. Sådan skriver du komplekse forespørgsler i SQL

  4. CROSS JOIN vs INNER JOIN i SQL