sql >> Databasteknik >  >> RDS >> Mysql

Förhindrar cirkulär sammanfogning, rekursiva sökningar

Om du använder MySQL 8.0 eller MariaDB 10.2 (eller högre) kan du prova rekursiva CTE:er (vanliga tabelluttryck) .

Förutsatt följande schema och 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 försöker du infoga en ny rad med child_id = 1 och parent_id = 4 . Men det skulle skapa cykliska relationer (1->4->2->1 och 1->4->3->1 ), som du vill förhindra. För att ta reda på om det redan finns en omvänd relation kan du använda följande fråga, som visar alla föräldrar till lista 4 (inklusive ärvda/transitiva föräldrar):

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 skulle bli:

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

Demo

Du kan se i resultatet att listan 1 är en av föräldrarna till lista 4 , och du skulle inte infoga den nya posten.

Eftersom du bara vill veta om lista 1 är i resultatet kan du ändra den sista raden till

select * from rcte where parent_id = @new_child_id limit 1

eller till

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

BTW:Du kan använda samma fråga för att förhindra redundanta relationer. Förutsatt att du vill infoga posten med child_id = 4 och parent_id = 1 . Detta skulle vara överflödigt eftersom lista 4 ärver redan lista 1 över lista 2 och lista 3 . Följande fråga skulle visa dig att:

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)

Och du kan använda en liknande fråga för att få alla ärvda föremål:

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. Optimistisk samtidighet:IsConcurrencyToken och RowVersion

  2. Vad används JPA @Table-anteckningskatalogen och schemavariablerna för?

  3. Hur hänger dessa tabeller ihop?

  4. Infogar från MS SQL Server till MySQL-databas