Saya memiliki tabel MySQL yaitu sebagai berikut:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
Sekarang, saya ingin memiliki satu permintaan MySQL yang saya sediakan dengan mudah [misalnya mengatakan 'id = 19'] maka saya harus mendapatkan semua id anak-anaknya [yaitu hasil harus memiliki id '20, 21,22 ']. ... Juga, hierarki anak-anak tidak diketahui itu dapat bervariasi ....
Juga, saya sudah memiliki solusi menggunakan for loop ..... Biarkan saya tahu bagaimana mencapai hal yang sama menggunakan permintaan MySQL tunggal jika memungkinkan.
mysql
sql
hierarchical-data
recursive-query
Tarun Parswani
sumber
sumber
Jawaban:
Untuk MySQL 8+: gunakan
with
sintaks rekursif .Untuk MySQL 5.x: gunakan variabel sebaris, ID jalur, atau gabung-sendiri.
MySQL 8+
Nilai yang ditentukan dalam
parent_id = 19
harus ditetapkan keid
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:
Ini biola .
Di sini, nilai yang ditentukan dalam
@pv := '19'
harus ditetapkan keid
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:
from
klausul dievaluasi pertama. Jadi disitulah awal@pv
diinisialisasi.where
klausul dievaluasi untuk setiap record dalam urutan pengambilan darifrom
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
).where
klausul ini dievaluasi secara berurutan, dan evaluasi terputus begitu hasil totalnya pasti. Oleh karena itu kondisi kedua harus di tempat kedua, karena menambahkan daftarid
ke induk, dan ini hanya akan terjadi jikaid
melewati kondisi pertama. Thelength
Fungsi hanya menelepon untuk memastikan kondisi ini selalu benar, bahkan jikapv
string yang akan untuk beberapa alasan menghasilkan nilai falsy.Secara keseluruhan, orang mungkin menganggap asumsi ini terlalu berisiko untuk diandalkan. The dokumentasi memperingatkan:
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 :
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:Maka Anda
select
akan terlihat seperti ini:Alternatif 3: Gabung-Sendiri Berulang
Jika Anda tahu batas atas untuk seberapa dalam hierarki hierarki Anda, Anda dapat menggunakan
sql
kueri standar seperti ini: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.sumber
parent_id > id
maka Anda tidak dapat menggunakan solusi ini.WITH RECURSIVE
metode ini, saya menemukan artikel berikut ini sangat membantu dengan berbagai skenario seperti kedalaman rekursi, perbedaan, dan mendeteksi dan menutup siklusDari blog Mengelola Data Hirarki di MySQL
Struktur meja
Pertanyaan:
Keluaran
Sebagian besar pengguna pada satu waktu atau yang lain telah berurusan dengan data hierarkis dalam database SQL dan tidak diragukan lagi mengetahui bahwa pengelolaan data hierarkis bukanlah tujuan dari basis data relasional. Tabel database relasional tidak hierarkis (seperti XML), tetapi hanya daftar datar. Data hierarkis memiliki hubungan orangtua-anak yang tidak secara alami direpresentasikan dalam tabel basis data relasional. Baca lebih lajut
Rujuk blog untuk lebih jelasnya.
EDIT:
Keluaran:
Referensi: Bagaimana cara melakukan permintaan SELECT Recursive di Mysql?
sumber
Coba ini:
Definisi tabel:
Baris eksperimental:
Prosedur tersimpan rekursif:
Fungsi pembungkus untuk prosedur tersimpan:
Pilih contoh:
Keluaran:
Memfilter baris dengan jalur tertentu:
Keluaran:
sumber
(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
Pendekatan terbaik yang saya buat adalah
Deskripsi pendekatan garis keturunan. dapat ditemukan di mana saja, misalnya Di Sini atau di sini . Pada fungsi - itulah yang membuat saya takut.
Pada akhirnya - mendapat solusi yang lebih atau kurang sederhana, relatif cepat, dan SIMPLE.
Fungsi tubuh
Dan kemudian Anda baru saja
Semoga ini bisa membantu seseorang :)
sumber
Melakukan hal yang sama untuk pertanyaan lain di sini
Mysql pilih rekursif buat semua anak dengan level ganda
Kueri akan:
sumber
SELECT idFolder, (SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM ( SELECT @pv:=(SELECT GROUP_CONCAT(idFolder SEPARATOR ',') FROM Folder WHERE idFolderParent IN (@pv)) AS lv FROM Folder JOIN (SELECT @pv:= F1.idFolder )tmp WHERE idFolderParent IN (@pv)) a) from folder F1 where id > 10
; Saya tidak bisa merujuk F1.idFolder untuk @pvNULL
sebagai hasilnya. Apakah Anda tahu mengapa itu bisa terjadi? Apakah ada prasyarat dalam hal mesin basis data, atau ada sesuatu yang berubah sejak Anda membuat jawaban ini yang membuat kueri ini usang?Jika Anda membutuhkan kecepatan baca cepat, opsi terbaik adalah menggunakan tabel penutupan. Tabel penutupan berisi baris untuk setiap pasangan leluhur / keturunan. Jadi dalam contoh Anda, tabel penutupan akan terlihat seperti
Setelah Anda memiliki tabel ini, kueri hierarki menjadi sangat mudah dan cepat. Untuk mendapatkan semua keturunan kategori 20:
Tentu saja, ada kerugian besar setiap kali Anda menggunakan data yang didenormalkan seperti ini. Anda perlu mempertahankan tabel penutupan di samping tabel kategori Anda. Cara terbaik mungkin menggunakan pemicu, tetapi agak rumit untuk melacak dengan benar sisipan / pembaruan / penghapusan untuk tabel penutupan. Seperti apa pun, Anda perlu melihat persyaratan Anda dan memutuskan pendekatan apa yang terbaik untuk Anda.
Sunting : Lihat pertanyaan Apa opsi untuk menyimpan data hierarkis dalam database relasional? untuk opsi lainnya. Ada berbagai solusi optimal untuk berbagai situasi.
sumber
Permintaan sederhana untuk mencantumkan anak rekursi pertama:
Hasil:
... dengan gabung kiri:
Solusi dari @tincot untuk mendaftar semua anak:
Uji secara online dengan Sql Fiddle dan lihat semua hasil.
http://sqlfiddle.com/#!9/a318e3/4/0
sumber
Anda dapat melakukannya seperti ini di database lain dengan cukup mudah dengan kueri rekursif (YMMV pada kinerja).
Cara lain untuk melakukannya adalah dengan menyimpan dua bit data tambahan, nilai kiri dan kanan. Nilai kiri dan kanan diturunkan dari traversal pre-order dari struktur pohon yang Anda wakili.
Ini dikenal sebagai Modifikasi Preorder Tree Traversal dan memungkinkan Anda menjalankan kueri sederhana untuk mendapatkan semua nilai induk sekaligus. Itu juga pergi dengan nama "set bersarang".
sumber
Cukup gunakan BlueM / tree kelas php untuk membuat pohon dari tabel relasi diri di mysql
Berikut adalah contoh penggunaan BlueM / tree:
sumber
Ini adalah tabel kategori .
Keluaran::
sumber
Ini sedikit rumit, periksa apakah ini berfungsi untuk Anda
SQL fiddle link http://www.sqlfiddle.com/#!2/e3cdf/2
Ganti dengan bidang dan nama tabel Anda dengan tepat.
sumber
Sesuatu yang tidak disebutkan di sini, meskipun sedikit mirip dengan alternatif kedua dari jawaban yang diterima tetapi berbeda dan berbiaya rendah untuk kueri hierarki besar dan item mudah (masukkan pembaruan dihapus), akan menambahkan kolom jalur persisten untuk setiap item.
beberapa seperti:
Contoh:
Optimalkan panjang jalur dan
ORDER BY path
gunakan pengkodean base36 sebagai ganti jalur numerik nyatahttps://en.wikipedia.org/wiki/Base36
Menekan juga pemisah slash '/' dengan menggunakan panjang tetap dan padding ke id yang disandikan
Penjelasan optimasi terperinci di sini: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
MELAKUKAN
membangun fungsi atau prosedur untuk membagi jalur bagi nenek moyang retreive dari satu item
sumber
base36
Ini bekerja untuk saya, semoga ini juga bekerja untuk Anda. Ini akan memberi Anda Record set Root to Child untuk Menu Tertentu apa pun. Ubah nama Field sesuai kebutuhan Anda.
sumber
Saya merasa lebih mudah untuk:
1) membuat fungsi yang akan memeriksa apakah suatu item ada di mana saja dalam hierarki induk dari yang lain. Sesuatu seperti ini (saya tidak akan menulis fungsinya, membuatnya dengan WHILE DO):
dalam contoh Anda
2) gunakan sub-pilih, sesuatu seperti ini:
sumber
Saya telah membuat permintaan untuk Anda. Ini akan memberi Anda Kategori Rekursif dengan Pertanyaan Tunggal:
Ini biola .
sumber