Untuk MySQL 8+: gunakan with
sintaks 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 = 19
harus ditetapkan ke id
dari 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 id
orang 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
from
klausul dievaluasi pertama. Jadi disitulah awal @pv
diinisialisasi.
- The
where
klausul dievaluasi untuk setiap record dalam urutan pengambilan dari from
alias. 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
where
klausul ini dievaluasi secara berurutan, dan evaluasi terputus begitu hasil totalnya pasti. Oleh karena itu kondisi kedua harus di tempat kedua, karena menambahkan daftar id
ke induk, dan ini hanya akan terjadi jika id
melewati kondisi pertama. The length
Fungsi hanya menelepon untuk memastikan kondisi ini selalu benar, bahkan jika pv
string 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 with
sintaks rekursif .
Efisiensi
Untuk kumpulan data yang sangat besar, solusi ini mungkin lambat, karena find_in_set
operasi 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 BY
klausa 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 id
nilai 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 select
akan 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 sql
kueri 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 where
menspesifikasikan kondisi yang orang tua Anda ingin mengambil keturunan. Anda dapat memperluas kueri ini dengan lebih banyak level sesuai kebutuhan.