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

Bagaimana cara membuat kueri rekursif hierarkis MySQL?

Untuk MySQL 8+: gunakan with rekursif sintaks.
Untuk MySQL 5.x: gunakan variabel sebaris, ID jalur, atau self-join.

MySQL 8+

with recursive cte (id, name, parent_id) as (
  select     id,
             name,
             parent_id
  from       products
  where      parent_id = 19
  union all
  select     p.id,
             p.name,
             p.parent_id
  from       products p
  inner join cte
          on p.parent_id = cte.id
)
select * from cte;

Nilai yang ditentukan dalam parent_id = 19 harus disetel ke id dari induk yang ingin Anda pilih semua keturunannya.

MySQL 5.x

Untuk versi MySQL yang tidak mendukung Ekspresi Tabel Umum (hingga versi 5.7), Anda dapat melakukannya dengan kueri berikut:

select  id,
        name,
        parent_id 
from    (select * from products
         order by parent_id, id) products_sorted,
        (select @pv := '19') initialisation
where   find_in_set(parent_id, @pv)
and     length(@pv := concat(@pv, ',', id))

Ini adalah biola .

Di sini, nilai yang ditentukan dalam @pv := '19' harus disetel ke id dari induk yang ingin Anda pilih semua keturunannya.

Ini akan berfungsi juga jika orang tua memiliki banyak anak-anak. Namun, setiap record harus memenuhi kondisi parent_id < id , jika tidak, hasilnya tidak akan lengkap.

Penetapan variabel di dalam kueri

Kueri ini menggunakan sintaks MySQL khusus:variabel ditetapkan dan dimodifikasi selama eksekusi. Beberapa asumsi dibuat tentang urutan eksekusi:

  • from klausa dievaluasi terlebih dahulu. Jadi di situlah @pv akan diinisialisasi.
  • where klausa dievaluasi untuk setiap record dalam urutan pengambilan dari from alias. Jadi, di sinilah suatu kondisi dimasukkan untuk hanya menyertakan catatan yang induknya telah diidentifikasi sebagai pohon turunan (semua keturunan dari induk utama secara bertahap ditambahkan ke @pv ).
  • Kondisi di where ini klausa dievaluasi secara berurutan, dan evaluasi dihentikan setelah hasil totalnya pasti. Oleh karena itu kondisi kedua harus di tempat kedua, karena menambahkan id ke daftar induk, dan ini hanya akan terjadi jika id melewati syarat pertama. length fungsi hanya dipanggil untuk memastikan kondisi ini selalu benar, bahkan jika pv string karena alasan tertentu akan menghasilkan nilai palsu.

Secara keseluruhan, orang mungkin menganggap asumsi ini terlalu berisiko untuk diandalkan. Dokumentasi memperingatkan:

Anda mungkin mendapatkan hasil yang Anda harapkan, tetapi ini tidak dijamin [...] urutan evaluasi untuk ekspresi yang melibatkan variabel pengguna tidak ditentukan.

Jadi meskipun bekerja secara konsisten dengan kueri di atas, urutan evaluasi mungkin masih berubah, misalnya saat Anda menambahkan kondisi atau menggunakan kueri ini sebagai tampilan atau subkueri dalam kueri yang lebih besar. Ini adalah "fitur" yang akan dihapus di masa mendatang Rilis MySQL :

Rilis MySQL sebelumnya memungkinkan untuk menetapkan nilai ke variabel pengguna dalam pernyataan selain SET . Fungsionalitas ini didukung di MySQL 8.0 untuk kompatibilitas mundur tetapi dapat dihapus pada rilis MySQL mendatang.

Seperti yang dinyatakan di atas, dari MySQL 8.0 dan seterusnya Anda harus menggunakan with rekursif sintaks.

Efisiensi

Untuk kumpulan data yang sangat besar, solusi ini mungkin menjadi lambat, karena find_in_set operasi bukanlah cara yang paling ideal untuk menemukan nomor dalam daftar, tentu saja tidak dalam daftar yang mencapai ukuran dalam urutan besarnya yang sama dengan jumlah catatan yang dikembalikan.

Alternatif 1:with recursive , connect by

Semakin banyak basis data yang mengimplementasikan SQL:1999 standar ISO WITH [RECURSIVE] sintaks untuk kueri rekursif (mis. Postgres 8.4+ , SQL Server 2005+ , DB2 , Oracle 11gR2+ , SQLite 3.8.4+ , Firebird 2.1+ , H2 , HyperSQL 2.1.0+ , Teradata , MariaDB 10.2.2+ ). Dan mulai versi 8.0, MySQL juga mendukungnya . Lihat bagian atas jawaban ini untuk sintaks yang digunakan.

Beberapa database memiliki sintaks alternatif non-standar untuk pencarian hierarkis, seperti CONNECT BY klausa tersedia di Oracle , DB2 , Informix , CUBRID dan basis data lainnya.

MySQL versi 5.7 tidak menawarkan fitur seperti itu. Ketika mesin database Anda menyediakan sintaks ini atau Anda dapat bermigrasi ke salah satu yang menyediakannya, maka itu tentu saja merupakan pilihan terbaik. Jika tidak, pertimbangkan juga alternatif berikut.

Alternatif 2:Pengenal gaya jalur

Segalanya menjadi jauh lebih mudah jika Anda menetapkan id nilai yang berisi informasi hierarkis:jalur. Misalnya, dalam kasus Anda ini bisa terlihat seperti ini:

ID NAME
19 kategori1
19/1 kategori2
19/1/1 kategori3
19/1/1/1 kategori4

Kemudian select akan terlihat seperti ini:

select  id,
        name 
from    products
where   id like '19/%'

Alternatif 3:Self-join berulang

Jika Anda mengetahui batas atas seberapa dalam hierarki pohon Anda, Anda dapat menggunakan sql standar pertanyaan seperti ini:

select      p6.parent_id as parent6_id,
            p5.parent_id as parent5_id,
            p4.parent_id as parent4_id,
            p3.parent_id as parent3_id,
            p2.parent_id as parent2_id,
            p1.parent_id as parent_id,
            p1.id as product_id,
            p1.name
from        products p1
left join   products p2 on p2.id = p1.parent_id 
left join   products p3 on p3.id = p2.parent_id 
left join   products p4 on p4.id = p3.parent_id  
left join   products p5 on p5.id = p4.parent_id  
left join   products p6 on p6.id = p5.parent_id
where       19 in (p1.parent_id, 
                   p2.parent_id, 
                   p3.parent_id, 
                   p4.parent_id, 
                   p5.parent_id, 
                   p6.parent_id) 
order       by 1, 2, 3, 4, 5, 6, 7;

Lihat biola ini

where condition menentukan induk mana yang ingin Anda ambil turunannya. Anda dapat memperluas kueri ini dengan lebih banyak level sesuai kebutuhan.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. UTF-8:Umum? Tempat sampah? Unikode?

  2. Bagaimana cara menguji apakah suatu string adalah JSON atau bukan?

  3. Contoh CURRENT_TIMESTAMP – MySQL

  4. Cara mereset kata sandi root MySQL

  5. SQL Query untuk mendapatkan nilai kolom yang sesuai dengan nilai MAX kolom lain?