Wenn das alles ist, was Sie brauchen, können Sie eine LIKE-Suche verwenden
SELECT *
FROM Table1
WHERE CELL LIKE 'AEE%';
Mit einem Index beginnend mit CELL
Dies ist eine Bereichsprüfung, die schnell ist.
Wenn Ihre Daten nicht so aussehen, können Sie einen path
erstellen Spalte, die wie ein Verzeichnispfad aussieht und alle Knoten "auf dem Weg/Pfad" von der Wurzel bis zum Element enthält.
| 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/ |
Um alle Nachkommen von 'AE' (einschließlich sich selbst) abzurufen, wäre Ihre Abfrage
SELECT *
FROM tree t
WHERE path LIKE '1/6/%';
oder (MySQL-spezifische Verkettung)
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
AND t.path LIKE CONCAT(r.path, '%');
Ergebnis:
| 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/ |
Leistung
Ich habe 100.000 Zeilen mit gefälschten Daten auf MariaDB erstellt mit dem sequence plugin mit dem folgenden Skript:
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.
Tests
Alle Elemente unter der Wurzel zählen:
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
Unterstrukturelemente unter einem bestimmten Knoten abrufen:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = '3B0'
AND t.path LIKE CONCAT(r.path, '%');
-- runtime: ~ 30 ms
Ergebnis:
| 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
Dies funktioniert auch für PostgreSQL. Nur die String-Verkettungssyntax muss geändert werden:
SELECT t.*
FROM tree t
CROSS JOIN tree r -- root
WHERE r.CELL = 'AE'
AND t.path LIKE r.path || '%';
Wie funktioniert die Suche
Wenn Sie sich das Testbeispiel ansehen, sehen Sie, dass alle Pfade im Ergebnis mit „1/4/11/14/614/4284/“ beginnen. Das ist der Pfad der Teilbaumwurzel mit CELL='3B0'
. Wenn der path
Spalte indiziert ist, findet die Engine sie alle effizient, da der Index nach path
sortiert ist . Es ist, als ob Sie in einem Wörterbuch mit 100.000 Wörtern alle Wörter finden möchten, die mit „pol“ beginnen. Sie müssten nicht das gesamte Wörterbuch lesen.