Untuk MySQL 8+: gunakan withsintaks rekursif .
Untuk MySQL 5.x: gunakan variabel sebaris, ID jalur, atau gabung-sendiri.
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 = 19harus ditetapkan ke iddari induk yang ingin Anda pilih semua keturunan.
MySQL 5.x
Untuk versi MySQL yang tidak mendukung Common Table Expressions (hingga versi 5.7), Anda akan mencapai ini 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 biola .
Di sini, nilai yang ditentukan dalam @pv := '19'harus ditetapkan ke idorang tua yang ingin Anda pilih semua keturunan.
Ini akan berfungsi juga jika orang tua memiliki beberapa anak. Namun, setiap catatan harus memenuhi persyaratan parent_id < id, jika tidak hasilnya tidak akan lengkap.
Tugas variabel di dalam kueri
Kueri ini menggunakan sintaks MySQL spesifik: variabel ditugaskan dan dimodifikasi selama eksekusi. Beberapa asumsi dibuat tentang urutan eksekusi:
- The
fromklausul dievaluasi pertama. Jadi disitulah awal @pvdiinisialisasi.
- The
whereklausul dievaluasi untuk setiap record dalam urutan pengambilan dari fromalias. Jadi disinilah syarat hanya memasukkan catatan-catatan yang induknya sudah diidentifikasi berada di pohon turunan (semua keturunan dari induk primer ditambahkan secara progresif @pv).
- Kondisi dalam
whereklausul ini dievaluasi secara berurutan, dan evaluasi terputus begitu hasil totalnya pasti. Oleh karena itu kondisi kedua harus di tempat kedua, karena menambahkan daftar idke induk, dan ini hanya akan terjadi jika idmelewati kondisi pertama. The lengthFungsi hanya menelepon untuk memastikan kondisi ini selalu benar, bahkan jika pvstring yang akan untuk beberapa alasan menghasilkan nilai falsy.
Secara keseluruhan, orang mungkin menganggap asumsi ini terlalu berisiko untuk diandalkan. The 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 itu bekerja secara konsisten dengan kueri di atas, urutan evaluasi masih dapat berubah, misalnya ketika Anda menambahkan kondisi atau menggunakan kueri ini sebagai tampilan atau sub-kueri dalam kueri yang lebih besar. Ini adalah "fitur" yang akan dihapus dalam rilis MySQL di masa mendatang :
Rilis MySQL sebelumnya memungkinkan untuk menetapkan nilai ke variabel pengguna dalam pernyataan selain SET. Fungsionalitas ini didukung dalam MySQL 8.0 untuk kompatibilitas ke belakang tetapi dapat dihapus dalam rilis MySQL berikutnya.
Seperti yang dinyatakan di atas, dari MySQL 8.0 dan seterusnya Anda harus menggunakan withsintaks rekursif .
Efisiensi
Untuk kumpulan data yang sangat besar, solusi ini mungkin lambat, karena find_in_setoperasi bukan 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 mengimplementasikan SQL: 1999 WITH [RECURSIVE]sintaks standar ISO 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 pada versi 8.0, MySQL juga mendukungnya . Lihat bagian atas jawaban ini untuk sintaks yang digunakan.
Beberapa database memiliki alternatif, sintaksis non-standar untuk pencarian hirarkis, seperti CONNECT BYklausa yang tersedia pada Oracle , DB2 , Informix , CUBRID dan database lainnya.
MySQL versi 5.7 tidak menawarkan fitur seperti itu. Ketika mesin database Anda menyediakan sintaks ini atau Anda dapat bermigrasi ke yang tidak, maka itu tentu saja merupakan pilihan terbaik. Jika tidak, maka pertimbangkan juga alternatif berikut.
Alternatif 2: Identifier Path-style
Banyak hal menjadi jauh lebih mudah jika Anda akan menetapkan idnilai yang berisi informasi hierarkis: jalan. Misalnya, dalam kasus Anda ini bisa terlihat seperti ini:
ID | NAME
19 | category1
19/1 | category2
19/1/1 | category3
19/1/1/1 | category4
Maka Anda selectakan terlihat seperti ini:
select id,
name
from products
where id like '19/%'
Alternatif 3: Gabung-Sendiri Berulang
Jika Anda tahu batas atas untuk seberapa dalam hierarki hierarki Anda, Anda dapat menggunakan sqlkueri standar 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
The wheremenspesifikasikan kondisi yang orang tua Anda ingin mengambil keturunan. Anda dapat memperluas kueri ini dengan lebih banyak level sesuai kebutuhan.