Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Mencegah penggabungan melingkar, pencarian rekursif

Jika Anda menggunakan MySQL 8.0 atau MariaDB 10.2 (atau lebih tinggi) Anda dapat mencoba CTE rekursif (ekspresi tabel umum) .

Dengan asumsi skema dan data berikut:

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

Sekarang Anda mencoba memasukkan baris baru dengan child_id = 1 dan parent_id = 4 . Tapi itu akan menciptakan hubungan siklik (1->4->2->1 dan 1->4->3->1 ), yang ingin Anda cegah. Untuk mengetahui apakah relasi terbalik sudah ada, Anda dapat menggunakan kueri berikut, yang akan menampilkan semua induk dari daftar 4 (termasuk orang tua yang diwariskan/transitif):

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

Hasilnya adalah:

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

Demo

Anda dapat melihat pada hasilnya, bahwa daftar 1 adalah salah satu orang tua dari daftar 4 , dan Anda tidak akan memasukkan catatan baru.

Karena Anda hanya ingin tahu apakah daftar 1 ada di hasilnya, Anda dapat mengubah baris terakhir menjadi

select * from rcte where parent_id = @new_child_id limit 1

atau ke

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

BTW:Anda dapat menggunakan kueri yang sama untuk mencegah hubungan yang berlebihan. Dengan asumsi Anda ingin memasukkan catatan dengan child_id = 4 dan parent_id = 1 . Ini akan berlebihan, karena daftar 4 sudah mewarisi daftar 1 lebih dari daftar 2 dan daftar 3 . Kueri berikut akan menunjukkan kepada Anda bahwa:

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)

Dan Anda dapat menggunakan kueri serupa untuk mendapatkan semua item yang diwarisi:

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. Pemetaan JPA:QuerySyntaxException:foobar tidak dipetakan...

  2. Menemukan postingan dengan tag1 DAN tag2? (menggunakan tabel gabungan) Ada / Memiliki / subqueries... Apa yang harus digunakan?

  3. Hapus semua baris dengan stempel waktu yang lebih lama dari x hari

  4. Proses pembunuhan MySQL adalah pengguna tidak mendapat cukup poin PHP

  5. Node.js terhubung melalui ssh