Jika hanya itu yang Anda butuhkan, Anda dapat menggunakan pencarian LIKE
SELECT *
FROM Table1
WHERE CELL LIKE 'AEE%';
Dengan indeks yang dimulai dengan CELL
ini adalah pemeriksaan jangkauan, yang cepat.
Jika data Anda tidak terlihat seperti itu, Anda dapat membuat path
kolom yang terlihat seperti jalur direktori dan berisi semua simpul "di jalan/jalur" dari root ke elemen.
| id | CELL | parent_id | path |
|====|======|===========|==========|
| 1 | A | NULL | 1/ |
| 2 | AA | 1 | 1/2/ |
| 3 | AAA | 2 | 1/2/3/ |
| 4 | AAC | 2 | 1/2/4/ |
| 5 | AB | 1 | 1/5/ |
| 6 | AE | 1 | 1/6/ |
| 7 | AEA | 6 | 1/6/7/ |
| 8 | AEE | 6 | 1/6/8/ |
| 9 | AEEB | 8 | 1/6/8/9/ |
Untuk mengambil semua turunan 'AE' (termasuk dirinya sendiri), kueri Anda adalah
SELECT *
FROM tree t
WHERE path LIKE '1/6/%';
atau (gabungan khusus MySQL)
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
AND t.path LIKE CONCAT(r.path, '%');
Hasil:
| id | CELL | parent_id | path |
|====|======|===========|==========|
| 6 | AE | 1 | 1/6/ |
| 7 | AEA | 6 | 1/6/7/ |
| 8 | AEE | 6 | 1/6/8/ |
| 9 | AEEB | 8 | 1/6/8/9/ |
Kinerja
Saya telah membuat 100 ribu baris data palsu di MariaDB dengan plugin urutan menggunakan skrip berikut:
drop table if exists tree;
CREATE TABLE tree (
`id` int primary key,
`CELL` varchar(50),
`parent_id` int,
`path` varchar(255),
unique index (`CELL`),
unique index (`path`)
);
DROP TRIGGER IF EXISTS `tree_after_insert`;
DELIMITER //
CREATE TRIGGER `tree_after_insert` BEFORE INSERT ON `tree` FOR EACH ROW BEGIN
if new.id = 1 then
set new.path := '1/';
else
set new.path := concat((
select path from tree where id = new.parent_id
), new.id, '/');
end if;
END//
DELIMITER ;
insert into tree
select seq as id
, conv(seq, 10, 36) as CELL
, case
when seq = 1 then null
else floor(rand(1) * (seq-1)) + 1
end as parent_id
, null as path
from seq_1_to_100000
;
DROP TRIGGER IF EXISTS `tree_after_insert`;
-- runtime ~ 4 sec.
Tes
Hitung semua elemen di bawah root:
SELECT count(*)
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = '1'
AND t.path LIKE CONCAT(r.path, '%');
-- result: 100000
-- runtime: ~ 30 ms
Dapatkan elemen subpohon di bawah simpul tertentu:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = '3B0'
AND t.path LIKE CONCAT(r.path, '%');
-- runtime: ~ 30 ms
Hasil:
| id | CELL | parent_id | path |
|=======|======|===========|=====================================|
| 4284 | 3B0 | 614 | 1/4/11/14/614/4284/ |
| 6560 | 528 | 4284 | 1/4/11/14/614/4284/6560/ |
| 8054 | 67Q | 6560 | 1/4/11/14/614/4284/6560/8054/ |
| 14358 | B2U | 6560 | 1/4/11/14/614/4284/6560/14358/ |
| 51911 | 141Z | 4284 | 1/4/11/14/614/4284/51911/ |
| 55695 | 16Z3 | 4284 | 1/4/11/14/614/4284/55695/ |
| 80172 | 1PV0 | 8054 | 1/4/11/14/614/4284/6560/8054/80172/ |
| 87101 | 1V7H | 51911 | 1/4/11/14/614/4284/51911/87101/ |
PostgreSQL
Ini juga berfungsi untuk PostgreSQL. Hanya sintaks rangkaian string yang harus diubah:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
AND t.path LIKE r.path || '%';
Bagaimana cara kerja pencarian
Jika Anda melihat contoh pengujian, Anda akan melihat bahwa semua jalur dalam hasil dimulai dengan '1/4/11/14/614/4284/'. Itu adalah path dari subtree root dengan CELL='3B0'
. Jika path
kolom diindeks, mesin akan menemukan semuanya secara efisien, karena indeks diurutkan berdasarkan path
. Sepertinya Anda ingin menemukan semua kata yang dimulai dengan 'pol' dalam kamus dengan 100 ribu kata. Anda tidak perlu membaca seluruh kamus.