Pomysł tej metody jest bardzo podobny a nawet bym powiedział powstał na bazie skryptu umieszczonego na stronie www.depesz.com. Metoda ta opiera się na konstrukcji dwóch tabel. W pierwszej (tree) będziemy przechowywać wszystkie parametry powiązane z poszczególnymi kategoriami. Przykładowo zamieszczę w niej tylko nazwę. W drugiej zaś (tree_pos) przechowywane będą powiązania pomiędzy poszczegółnymi elementami (jeżeli oczywiście takowe istnieją). Oto przykład struktury dla tej metody:
id SERIAL PRIMARY KEY,
nazwa VARCHAR(128) NOT NULL
);
id SERIAL PRIMARY KEY,
tree_id INTEGER NOT NULL,
parent_id INTEGER NOT NULL,
depth INTEGER NOT NULL
);
W stosunku do rozwiązania na stronie www.depesz.com moja metoda różni się tym że w tabeli tree dodałem pole tree_id któe to może być null:
ALTER TABLE tree ADD CONSTRAINT fk_tree_id FOREIGN KEY (tree_id) REFERENCES tree(id);
Oczywiście tak jak to opisuje autor rozwiązania, metoda ta posiada
minus taki, że w tabeli tree_pos przechowywane są informacje
nadmiarowe. Jednak ich nadmiarowość przekłada się na wydajność i
możliwości. Implementacja przenoszenia elementów pomiędzy kategoriami
może się okazać zbyt skomplikowana. Doświadczyłem tego podczas
implementacji tego rozwiązania. W związku z tym aby sobie trochę pomóc
dodałem jeszcze jedno pole w tabeli tree w któym będę przechowywał id
rodzica. Dzięki temu bez problemu mogę na tabeli tree_pos dokonywać
wszelkich operacji UPDATE i DELETE zachowując informacje o konstrukcji
drzewa.
Zaletą tego rozwiązania jest prostota w implementacji. Możemy tutaj bardzo szybko wyszukiwać kategorie o zadanym poziomie zagnieżdżenia. Banalne jest rónież wyświetlanie ścieżki. Łatwo można wyszukiwać elementy podpięte do kategorii na dowolnym poziomie jak rónież szybko odnaleźć rodzica z dowolnego poziomu. Skomplikowane jak się może okazać jest jedynie przenoszenie elementów. Jednakże z pomocą kilku procedur jakie napisałem w PLPGSQL`u może się okazać wprost trywialne.
No dobrze a teraz będę się tłumaczył z konstrukcji zastosowania tej metody.
W metodzie tej struktura naszego drzewa przechowywana będzie w następujący sposób:
test=# select * from tree; id | nazwa | tree_id ----+------------------+--------- 1 | Kategoria główna | 2 | Kat 1 | 1 3 | Kat 1.1 | 2 4 | Kat 1.2 | 2 5 | Kat 2 | 1 6 | Kat 2.1 | 5 7 | Kat 2.2 | 5 8 | Kat 2.3 | 5 9 | Kat 2.1.1 | 6 10 | Kat 2.1.2 | 6 11 | Kat 3 | 1 (11 rows) |
test=# select * from tree_pos; id | tree_id | parent_id | depth ----+---------+-----------+------- 3 | 1 | 1 | 0 4 | 2 | 2 | 0 5 | 2 | 1 | 1 6 | 3 | 3 | 0 7 | 3 | 2 | 1 8 | 3 | 1 | 2 9 | 4 | 4 | 0 10 | 4 | 2 | 1 11 | 4 | 1 | 2 12 | 5 | 5 | 0 13 | 5 | 1 | 1 14 | 6 | 6 | 0 15 | 6 | 5 | 1 16 | 6 | 1 | 2 17 | 7 | 7 | 0 18 | 7 | 5 | 1 19 | 7 | 1 | 2 20 | 8 | 8 | 0 21 | 8 | 5 | 1 22 | 8 | 1 | 2 23 | 9 | 9 | 0 24 | 9 | 6 | 1 25 | 9 | 5 | 2 26 | 9 | 1 | 3 27 | 10 | 10 | 0 28 | 10 | 6 | 1 29 | 10 | 5 | 2 30 | 10 | 1 | 3 31 | 11 | 11 | 0 32 | 11 | 1 | 1 (30 rows) |
Jak już wspomniałem w tabelce tree_pos przechowywane są informacje o powiązaniach poszczególnych elementów z ich rodzicami (również dziadkami).
Wykonując teraz zapytanie na naszej tabeli tree_pos:
select * from tree_pos where tree_id=9;
dostaniemy wszystkich rodziców elementu "Kat 2.1.1":
----+---------+-----------+-------
23 | 9 | 9 | 0
24 | 9 | 6 | 1
25 | 9 | 5 | 2
26 | 9 | 1 | 3
(4 rows)
Rozbudowując nasze zapytanie do postaci:
otrzymujemy pełną ścieżkę począwszy od głównego korzenia:
------------------
Kategoria główna
Kat 2
Kat 2.1
Kat 2.1.1
(4 rows)
Na podstawie tych danych możemy łatwo utworzyć taką ścieżkę:
Kategoria główna » Kat 2 » Kat 2.1 » Kat 2.1.1
Wydaje mi się, że udało mi się dobrze opisać tę metodę przechowywania. Przejdźmy zatem do implementacji.