Es gibt wirklich gute Lösungen, die die interne Btree-Darstellung von Sql-Indizes ausnutzen. Dies basiert auf einer großartigen Forschungsarbeit, die um 1998 herum durchgeführt wurde.
Hier ist eine Beispieltabelle (in mysql).
CREATE TABLE `node` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`tw` int(10) unsigned NOT NULL,
`pa` int(10) unsigned DEFAULT NULL,
`sz` int(10) unsigned DEFAULT NULL,
`nc` int(11) GENERATED ALWAYS AS (tw+sz) STORED,
PRIMARY KEY (`id`),
KEY `node_tw_index` (`tw`),
KEY `node_pa_index` (`pa`),
KEY `node_nc_index` (`nc`),
CONSTRAINT `node_pa_fk` FOREIGN KEY (`pa`) REFERENCES `node` (`tw`) ON DELETE CASCADE
)
Die einzigen für die Baumdarstellung erforderlichen Felder sind:
- tw: Der DFS-Index für die Vorordnung von links nach rechts, wobei Root = 1 ist.
- pa: Der Verweis (mit tw) auf den übergeordneten Knoten, Root hat null.
- sz: Die Größe der Verzweigung des Knotens einschließlich seiner selbst.
- nc: wird als syntaktischer Zucker verwendet. Er ist tw+sz und stellt den tw des "nächsten Kindes" des Knotens dar.
Hier ist ein Beispiel für eine Population von 24 Knoten, geordnet nach tw:
+-----+---------+----+------+------+------+
| id | name | tw | pa | sz | nc |
+-----+---------+----+------+------+------+
| 1 | Root | 1 | NULL | 24 | 25 |
| 2 | A | 2 | 1 | 14 | 16 |
| 3 | AA | 3 | 2 | 1 | 4 |
| 4 | AB | 4 | 2 | 7 | 11 |
| 5 | ABA | 5 | 4 | 1 | 6 |
| 6 | ABB | 6 | 4 | 3 | 9 |
| 7 | ABBA | 7 | 6 | 1 | 8 |
| 8 | ABBB | 8 | 6 | 1 | 9 |
| 9 | ABC | 9 | 4 | 2 | 11 |
| 10 | ABCD | 10 | 9 | 1 | 11 |
| 11 | AC | 11 | 2 | 4 | 15 |
| 12 | ACA | 12 | 11 | 2 | 14 |
| 13 | ACAA | 13 | 12 | 1 | 14 |
| 14 | ACB | 14 | 11 | 1 | 15 |
| 15 | AD | 15 | 2 | 1 | 16 |
| 16 | B | 16 | 1 | 1 | 17 |
| 17 | C | 17 | 1 | 6 | 23 |
| 359 | C0 | 18 | 17 | 5 | 23 |
| 360 | C1 | 19 | 18 | 4 | 23 |
| 361 | C2(res) | 20 | 19 | 3 | 23 |
| 362 | C3 | 21 | 20 | 2 | 23 |
| 363 | C4 | 22 | 21 | 1 | 23 |
| 18 | D | 23 | 1 | 1 | 24 |
| 19 | E | 24 | 1 | 1 | 25 |
+-----+---------+----+------+------+------+
Jedes Baumergebnis kann nicht-rekursiv durchgeführt werden. Zum Beispiel, um eine Liste der Vorfahren des Knotens tw='22' zu erhalten
Vorfahren
select anc.* from node me,node anc
where me.tw=22 and anc.nc >= me.tw and anc.tw <= me.tw
order by anc.tw;
+-----+---------+----+------+------+------+
| id | name | tw | pa | sz | nc |
+-----+---------+----+------+------+------+
| 1 | Root | 1 | NULL | 24 | 25 |
| 17 | C | 17 | 1 | 6 | 23 |
| 359 | C0 | 18 | 17 | 5 | 23 |
| 360 | C1 | 19 | 18 | 4 | 23 |
| 361 | C2(res) | 20 | 19 | 3 | 23 |
| 362 | C3 | 21 | 20 | 2 | 23 |
| 363 | C4 | 22 | 21 | 1 | 23 |
+-----+---------+----+------+------+------+
Geschwister und Kinder sind trivial - verwenden Sie einfach pa field ordering by tw.
Nachkommenschaft
Zum Beispiel die Menge (Zweig) der Knoten, die in tw = 17 verwurzelt sind.
select des.* from node me,node des
where me.tw=17 and des.tw < me.nc and des.tw >= me.tw
order by des.tw;
+-----+---------+----+------+------+------+
| id | name | tw | pa | sz | nc |
+-----+---------+----+------+------+------+
| 17 | C | 17 | 1 | 6 | 23 |
| 359 | C0 | 18 | 17 | 5 | 23 |
| 360 | C1 | 19 | 18 | 4 | 23 |
| 361 | C2(res) | 20 | 19 | 3 | 23 |
| 362 | C3 | 21 | 20 | 2 | 23 |
| 363 | C4 | 22 | 21 | 1 | 23 |
+-----+---------+----+------+------+------+
Zusätzliche Hinweise
Diese Methode ist äußerst nützlich, wenn die Zahl der Lesevorgänge die der Einfügungen oder Aktualisierungen weit übersteigt.
Da das Einfügen, Verschieben oder Aktualisieren eines Knotens im Baum eine Anpassung des Baums erfordert, muss die Tabelle gesperrt werden, bevor mit der Aktion begonnen wird.
Die Kosten für das Einfügen/Löschen sind hoch, da der tw-Index und die sz-Werte (Zweiggröße) für alle Knoten nach dem Einfügepunkt bzw. für alle Vorfahren aktualisiert werden müssen.
Beim Verschieben einer Verzweigung wird der tw-Wert der Verzweigung aus dem Bereich verschoben, so dass beim Verschieben einer Verzweigung auch die Fremdschlüssel-Beschränkungen deaktiviert werden müssen. Zum Verschieben einer Verzweigung sind im Wesentlichen vier Abfragen erforderlich:
- Bewegen Sie den Zweig aus der Reichweite.
- Schließen Sie die Lücke, die sie hinterlassen hat. (der verbleibende Baum ist jetzt normalisiert).
- Öffnen Sie die Lücke, in die er gehen soll.
- Bringen Sie den Zweig an seine neue Position.
Baumabfragen anpassen
Das Öffnen/Schließen von Lücken im Baum ist eine wichtige Unterfunktion, die von den Methoden "Erstellen/Aktualisieren/Löschen" verwendet wird, weshalb ich sie hier einfüge.
Wir benötigen zwei Parameter - ein Flag, das angibt, ob wir die Größe verkleinern oder vergrößern, und den tw-Index des Knotens. Zum Beispiel tw=18 (mit einer Zweiggröße von 5). Nehmen wir an, dass wir eine Verkleinerung vornehmen (tw entfernen) - das bedeutet, dass wir in den Aktualisierungen des folgenden Beispiels "-" statt "+" verwenden.
Wir verwenden zunächst eine (leicht abgewandelte) Vorgängerfunktion, um den Wert sz zu aktualisieren.
update node me, node anc set anc.sz = anc.sz - me.sz from
node me, node anc where me.tw=18
and ((anc.nc >= me.tw and anc.tw < me.pa) or (anc.tw=me.pa));
Dann müssen wir den tw für diejenigen anpassen, deren tw höher ist als der zu entfernende Zweig.
update node me, node anc set anc.tw = anc.tw - me.sz from
node me, node anc where me.tw=18 and anc.tw >= me.tw;
Dann müssen wir die Eltern für diejenigen anpassen, deren pa's tw höher ist als der zu entfernende Zweig.
update node me, node anc set anc.pa = anc.pa - me.sz from
node me, node anc where me.tw=18 and anc.pa >= me.tw;