Struktura drzewa w MySQL. Michał Tyszczenko

Podobne dokumenty
Przykłady najlepiej wykonywać od razu na bazie i eksperymentować z nimi.

Wykład 05 Bazy danych

Relacyjne bazy danych. Podstawy SQL

Wykład 6. SQL praca z tabelami 3

Wykład 5. SQL praca z tabelami 2

Ćwiczenia laboratoryjne nr 11 Bazy danych i SQL.

Relacyjne bazy danych. Podstawy SQL

Język SQL, zajęcia nr 1

Laboratorium nr 4. Temat: SQL część II. Polecenia DML

Modelowanie hierarchicznych struktur w relacyjnych bazach danych

Bazy Danych - Instrukcja do Ćwiczenia laboratoryjnego nr 8

strukturalny język zapytań używany do tworzenia i modyfikowania baz danych oraz do umieszczania i pobierania danych z baz danych

SIECI KOMPUTEROWE I BAZY DANYCH

Bazy danych. Polecenia SQL

Wykład 8. SQL praca z tabelami 5

Paweł Rajba

SQL (ang. Structured Query Language)

Autor: Joanna Karwowska

Bazy Danych i Usługi Sieciowe

Ref. 7 - Język SQL - polecenia DDL i DML

Projektowanie systemów baz danych

Instalacja MySQL.

Wstęp do relacyjnych baz danych. Jan Bartoszek

Bazy danych i usługi sieciowe

Bazy danych. Bazy danych. Podstawy języka SQL. Dr inż. Paweł Kasprowski.

PRZESTRZENNE BAZY DANYCH WYKŁAD 2

Programowanie MSQL. show databases; - pokazanie jakie bazy danych są dostępne na koncie

1. Połączenie z bazą danych. W wybranym edytorze tworzymy plik sqltest.py i umieszczamy w nim poniższy kod. #!/usr/bin/python3 import sqlite3

Język SQL Złączenia. Laboratorium. Akademia Morska w Gdyni

Autor: Joanna Karwowska

Bazy danych. dr inż. Arkadiusz Mirakowski

1. Tworzenie tabeli. 2. Umieszczanie danych w tabeli

Autor: Joanna Karwowska

Aby uruchomić program klienta i połączyć się z serwerem, należy komendę:

Bazy danych dla producenta mebli tapicerowanych. Bartosz Janiak Marcin Sikora Wrocław r.

77. Modelowanie bazy danych rodzaje połączeń relacyjnych, pojęcie klucza obcego.

Bazy danych. Wykład IV SQL - wprowadzenie. Copyrights by Arkadiusz Rzucidło 1

Bazy danych 10. SQL Widoki

Wyzwalacze (triggery) Przykład

Tworzenie tabeli przez select CREATE TABLE PRAC2 AS SELECT P.NAZWISKO, Z.NAZWA FROM PRAC P NATURAL JOIN ZESP Z

Kurs. Podstawy MySQL

Bazy danych. Dr inż. Paweł Kasprowski

Język DML. Instrukcje DML w różnych implementacjach SQL są bardzo podobne. Podstawowymi instrukcjami DML są: SELECT INSERT UPDATE DELETE

Systemy internetowe. Wykład 4 mysql. West Pomeranian University of Technology, Szczecin; Faculty of Computer Science

Język SQL, zajęcia nr 2

Systemy GIS Tworzenie zapytań w bazach danych

CREATE DATABASE ksiegarnia_internetowa DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

Oracle PL/SQL. Paweł Rajba.

Informatyka (5) SQL. dr inż. Katarzyna Palikowska Katedra Transportu Szynowego p. 4 Hydro

1: 2: 3: 4: 5: 6: 7: 8: 9: 10:

Przykładowa baza danych BIBLIOTEKA

KOLEKCJE - to typy masowe,zawierające pewną liczbę jednorodnych elementów

BAZY DANYCH. CREATE TABLE dbo.wydzialy (ID INT, Akronim VARCHAR(4) NOT NULL, Wydzial VARCHAR(30) NOT NULL, CONSTRAINT Kluczyk PRIMARY KEY(ID) )

ACESS- zadania z wykorzystaniem poleceń SQL

Wstęp 5 Rozdział 1. Podstawy relacyjnych baz danych 9

Języki programowania wysokiego poziomu. PHP cz.4. Bazy danych

Literatura: SQL Ćwiczenia praktyczne Autor: Marcin Lis Wydawnictwo: Helion. Autor: Joanna Karwowska

Internetowe bazy danych

Bazy danych 7. SQL podstawy

Wprowadzenie do projektowania i wykorzystania baz danych Relacje

SQL :: Data Definition Language

Wdrożenie modułu płatności eservice. dla systemu Gekosale 1.4

Bazy Danych. SQL Podstawy języka III: powtórzenie. Krzysztof Regulski WIMiIP, KISiM, B5, pok. 408

Część 1: OLAP. Raport z zajęć laboratoryjnych w ramach przedmiotu Hurtownie i eksploracja danych

Blaski i cienie wyzwalaczy w relacyjnych bazach danych. Mgr inż. Andrzej Ptasznik

Wprowadzenie do BD Operacje na bazie i tabelach Co poza zapytaniami? Algebra relacji. Bazy Danych i Systemy informacyjne Wykład 2.

Przestrzenne bazy danych Podstawy języka SQL

P o d s t a w y j ę z y k a S Q L

Bazy danych Ćwiczenia projektowe

3. Podzapytania, łączenie tabel i zapytań

Wprowadzenie do baz danych

Konstruowanie Baz Danych SQL UNION, INTERSECT, EXCEPT

Hurtownia Świętego Mikołaja projekt bazy danych

koledzy, Jan, Nowak, ul. Niecała 8/23, , Wrocław, , ,

Bazy danych. Bazy danych. Zapytania SELECT. Dr inż. Paweł Kasprowski.

1. Sprawdzenie ustawień konfiguracyjnych. Uruchomienie wiersza poleceń:..\ścieżka\bin>mysqladmin variables

Podstawy języka SQL. standardy SQL formułowanie zapytań operacje na strukturach danych manipulowanie danymi. Bazy danych s.5-1

Wprowadzenie do JDBC z wykorzystaniem bazy H2

Programowanie w Ruby

Grupowanie i funkcje agregujące

Informatyka sem. III studia inżynierskie Transport 2018/19 LAB 2. Lab Backup bazy danych. Tworzenie kopii (backup) bazy danych

Ćwiczenie zapytań języka bazy danych PostgreSQL

Informatyka 3 : Instrukcja 4

Przykład 3 Zdefiniuj w bazie danych hurtownia_nazwisko przykładową funkcję użytkownika fn_rok;

Instytut Mechaniki i Inżynierii Obliczeniowej fb.com/groups/bazydanychmt/

BAZA DANYCH SIECI HOTELI

Instrukcja podwaja zarobki osób, których imiona zaczynają się P i dalsze litery alfabetu zakładamy, że takich osbób jest kilkanaście.

Informatyka 3 : Instrukcja 4 / 5

Połączenie z bazą danych : mysql h u root -p Enter password: *******

Podstawy języka SQL. SQL Structured Query Languagestrukturalny

Wykład 5: PHP: praca z bazą danych MySQL

Bazy danych. dr Radosław Matusik. radmat

3 Przygotowali: mgr inż. Barbara Łukawska, mgr inż. Maciej Lasota

Tworzenie tabel. Bazy danych - laboratorium, Hanna Kleban 1

Widok Connections po utworzeniu połączenia. Obszar roboczy

Bazy danych SQL Server 2005

Modelowanie wymiarów

SQL - Structured Query Language -strukturalny język zapytań SQL SQL SQL SQL

Transkrypt:

Struktura drzewa w MySQL Michał Tyszczenko

W informatyce drzewa są strukturami danych reprezentującymi drzewa matematyczne. W naturalny sposób reprezentują hierarchię danych toteż głównie do tego celu są stosowane. Drzewa ułatwiają i przyspieszają wyszukiwanie, a także pozwalają w łatwy sposób operować na posortowanych danych. Znaczenie tych struktur jest bardzo duże i ze względu na swoje własności drzewa są stosowane praktycznie w każdej dziedzinie informatyki. Przechowywanie drzew w bazie danych okazuje się nie być takie proste, ponieważ ani System Zarządzania Relacyjnymi Bazami Danych MySQL ani sam język SQL nie posiadają żadnych wbudowanych mechanizmów do przechowywania oraz zarządzania takimi strukturami. Powstało jednak wiele różnych implementacji takich drzew, z których żadna nie jest idealna. O wyborze metody może decydować łatwość oraz szybkość wykonania podstawowych operacji tj: wyliczenie wszystkich elementów drzewa, wyszukanie konkretnego elementu, dodanie nowego elementu w określonym miejscu drzewa, usunięcie elementu. 1 Chciałbym przedstawić oraz porównać dwie metody zarządzania strukturami drzewa w MySQL. W obu metodach wykorzystam drzewo kategorii od A do L ukazane na rysunku poniżej. 1 http://pl.wikipedia.org/wiki/drzewo_(informatyka) 2008-03-31 godz. 21:00

Metoda 1 1. Struktura tabeli Informacje o drzewie przechowywane będą w tabeli o nazwie Kategorie. Pole id nazwa id_rodzica Tabela Kategorie Typ INTEGER(8) VARCHAR(100) INTEGER(8) W języku SQL tabelę tworzymy za pomocą zapytania: CREATE TABLE `Kategorie` ( `id` int(8) NOT NULL AUTO_INCREMENT, `nazwa` varchar( 100 ) NOT NULL DEFAULT '', `id_rodzica` int(8) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ); 2. Dodanie rekordów do tabeli Wykonujemy zapytanie: INSERT INTO `Kategorie` VALUES ('1', 'A', '0'), ('2', 'B', '1'), ('3', 'C', '1'), ('4', 'D', '1'), ('5', 'E', '2'), ('6', 'F', '3'), ('7', 'G', '3'), ('8', 'H', '4'), ('9', 'I', '4'), ('10', 'J', '8'), ('11', 'K', '8'), ('12', 'L', '8');

Po wykonaniu zapytania i wyświetleniu wszystkich rekordów otrzymujemy: +----------------+ id nazwa id_rodzica +----------------+ 1 A 0 2 B 1 3 C 1 4 D 1 5 E 2 6 F 3 7 G 3 8 H 4 9 I 4 10 J 8 11 K 8 12 L 8 +----------------+ Na rysunku widzimy odpowiednio ponumerowane drzewo. 3. Wyświetlenie całego drzewa Wyświetlenie całego drzewa jest dość skomplikowane, ponieważ musimy wielokrotnie złączyć tabelę tyle razy, ile poziomów posiada nasze drzewo. Tak więc nie jest to zapytanie uniwersalne, za każdym razem kiedy zmieni się ilość poziomów drzewa, zapytanie to będzie trzeba dostosowywać.

SELECT t1.nazwa AS poziom1, t2.nazwa as poziom2, t3.nazwa as poziom3, t4.nazwa as poziom4 FROM Kategorie AS t1 LEFT JOIN Kategorie AS t2 ON (t2.id_rodzica = t1.id) LEFT JOIN Kategorie AS t3 ON (t3.id_rodzica = t2.id) LEFT JOIN Kategorie AS t4 ON (t4.id_rodzica = t3.id) WHERE t1.nazwa = 'A'; Po wykonaniu zapytania otrzymujemy: +---------+---------+---------+---------+ poziom1 poziom2 poziom3 poziom4 +---------+---------+---------+---------+ A B E NULL A C F NULL A C G NULL A D H J A D H K A D H L A D I NULL +---------+---------+---------+---------+ 4. Znalezienie wszystkich liści drzewa Aby znaleźć wszystkie liście wykonujemy zapytanie SQL, które szuka wszystkich elementów drzewa nie posiadających dzieci. Zapytanie to jest uniwersalne dla każdego drzewa, niezależnie do jego rozmiarów, poziomów czy zagłębień. Oczywiście dla rozległych drzew będzie wykonywało się odpowiednio wolniej. SELECT t1.nazwa FROM Kategorie AS t1 LEFT JOIN Kategorie as t2 ON (t1.id = t2.id_rodzica) WHERE t2.id IS NULL; Wynik zapytania przedstawiony w tabeli i na rysunku. nazwa E F G I J K L

5. Znalezienie drogi od określonego miejsc do korzenia Aby znaleźć drogę od określonego miejsc drzewa do korzenia należy wykonać zapytanie zbudowane w ten sam sposób jak zapytanie wyświetlające całe drzewo. Jako warunek podajemy element od którego i do którego szukamy, czyli zakres przeszukiwania. Również ten SQL nie jest uniwersalny i wymaga wielokrotnych złączeń tabeli w zależności od ilości poziomów drzewa. SELECT t1.nazwa AS p_1, t2.nazwa as p_2, t3.nazwa as p_3, t4.nazwa as p_4 FROM Kategorie AS t1 LEFT JOIN Kategorie AS t2 ON (t2.id_rodzica = t1.id) LEFT JOIN Kategorie AS t3 ON (t3.id_rodzica = t2.id) LEFT JOIN Kategorie AS t4 ON (t4.id_rodzica = t3.id) WHERE t1.nazwa = 'A' AND t4.nazwa = 'K'; Wynik zapytania ukazany w tabeli oraz na rysunku: +-----+------+------+------+ p_1 p_2 p_3 p_4 +-----+------+------+------+ A D H K +-----+------+------+------+ 6. Dodanie nowej kategorii W metodzie tej, dodawanie nowej pozycji do drzewa jest bardzo proste i szybkie. Aby tego dokonać należy dodać nowy rekord do tabeli Kategorie, gdzie jako id_rodzica podajemy id elementu nadrzędnego. Np. aby dodać elementy M i N wykonujemy zapytania: INSERT INTO Kategorie (nazwa, id_rodzica) VALUES ('M',6); INSERT INTO Kategorie (nazwa, id_rodzica) VALUES ('N',11);

Drzewo wraz z nowymi elementami: 7. Przenoszenie kategorii Przenoszenie, tak jak dodawanie, jest również szybkie i proste. Dodatkową zaletą jest możliwość przeniesienia całej gałęzi przenosząc tylko jeden element. Wynika to z tego, iż każdy element zna tylko swojego rodzica, a więc przenosząc dowolny element drzewo nie traci spójności. Np. przenosząc element H wraz z nim przeniosą się wszystkie elementy poniżej niego.

Do tego celu użyjemy zapytania, które odczyta do zmiennej id elementu G, do którego przenosimy element H oraz zapytania, które zmieni id_rodzica w elemencie H na id elementy G. SELECT @id := id FROM Kategorie WHERE nazwa = 'G'; UPDATE Kategorie SET id_rodzica = @id WHERE nazwa = 'H'; W ten sposób w drzewie zmieniła się ilość poziomów, dlatego aby ponownie wyświetlić całe drzewo, należy do zwiększyć ilość złączeń tabel. SELECT t1.nazwa AS poziom1, t2.nazwa as poziom2, t3.nazwa as poziom3, t4.nazwa as poziom4, t5.nazwa as poziom5, t6.nazwa as poziom6 FROM Kategorie2 AS t1 LEFT JOIN Kategorie AS t2 ON (t2.id_rodzica = t1.id) LEFT JOIN Kategorie AS t3 ON (t3.id_rodzica = t2.id) LEFT JOIN Kategorie AS t4 ON (t4.id_rodzica = t3.id) LEFT JOIN Kategorie AS t5 ON (t5.id_rodzica = t4.id) LEFT JOIN Kategorie AS t6 ON (t6.id_rodzica = t5.id) WHERE t1.nazwa = 'A'; Wynik zapytania: +---------+---------+---------+---------+---------+---------+ poziom1 poziom2 poziom3 poziom4 poziom5 poziom6 +---------+---------+---------+---------+---------+---------+ A B E NULL NULL NULL A C F M NULL NULL A C G H J NULL A C G H K N A C G H L NULL A D I NULL NULL NULL +---------+---------+---------+---------+---------+---------+

Metoda 2. 1. Struktura tabeli W metodzie tej dane przechowywane będą w tabeli, która składać się z 4 pól. Zamiast id_rodzica każdy element będzie posiadał pola lewa oraz prawa. Pole id nazwa lewa prawa Tabela Kategorie Typ INTEGER(8) VARCHAR(100) INTEGER(8) INTEGER(8) Tabelą tworzymy za pomocą zapytania: CREATE TABLE `Kategorie` ( `id` int(8) NOT NULL AUTO_INCREMENT, `nazwa` varchar( 100 ) NOT NULL DEFAULT '', `lewa` int(8) NOT NULL DEFAULT '0', `prawa` int(8) NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `lewa` ( lewa`), KEY `prawa` (`prawa`) ); 2. Dodanie rekordów Przed dodaniem rekordów do tabeli numerujemy drzewo, w sposób widoczny na rysunku:

Jak numerujemy? Każda kategoria posiada dwa pola: lewa i prawa Numerowanie zaczynamy od korzenia kategorii, gdzie: lewa = 1 Jeżeli kategoria nie posiada żadnych podkategorii to: prawa = lewa + 1 Jeżeli kategoria posiada podkategorie to: prawa = lewa + (2 * liczba_podkategorii + 1) Stąd: liczba_podkategorii = (prawa lewa 1)/2 Czyli: Dla pierwszego dziecka: lewa = lewa_rodzica + 1 Dla rodzica: prawa = prawa_ostatniego_dziecka + 1 Wykonujemy zapytanie: INSERT INTO `Kategorie` VALUES ('1', 'A', '1', '24'), ('2', 'B', '2', '5'), ('3', 'C', '6', '11'), ('4', 'D', '12', '23'), ('5', 'E', '3', '4'), ('6', 'F', '7', '8'), ('7', 'G', '9', '10'), ('8', 'H', '13', '20'), ('9', 'I', '21', '22'), ('10', 'J', '14', '15'), ('11', 'K', '16', '17'), ('12', 'L', '18', '19');

Po wykonaniu zapytania tabela wygląda w następujący sposób: +---------- id nazwa lewa prawa +---------- 1 A 1 24 2 B 2 5 3 C 6 11 4 D 12 23 5 E 3 4 6 F 7 8 7 G 9 10 8 H 13 20 9 I 21 22 10 J 14 15 11 K 16 17 12 L 18 19 +---------- 3. Wyświetlenie całego drzewa przy pomocy jednego zapytania W metodzie tej zapytanie pobierające informacje o całym drzewie jest uniwersale i nie zależy od ilość poziomów czy elementów. Zaczyna szukać od korzenia, a lewa każdego dziecka musi zawierać się pomiędzy lewą a prawą rodzica. Wykonując zapytanie: SELECT dziecko.nazwa FROM Kategorie AS dziecko, Kategorie AS rodzic WHERE (dziecko.lewa BETWEEN rodzic.lewa AND rodzic.prawa) AND rodzic.nazwa = 'A' ORDER BY dziecko.lewa; Otrzymujemy: nazwa A B E C F G D H J K L I

4. Znalezienie wszystkich liści drzewa W metodzie tej, każdy liść (element nie posiadający dzieci) posiada taką cechę, że jego prawa strona wynosi lewa+1, dlatego podając taki warunek w zapytaniu SQL znajdziemy w prosty sposób wszystkie liście, niezależnie od wielkości drzewa. Zapytanie: SELECT nazwa FROM Kategorie WHERE prawa = lewa + 1; Wynik zapytania przedstawiony w tabeli i na rysunku: nazwa E F G I J K L 5. Znalezienie drogi od określonego miejsca do korzenia Aby znaleźć drogę od danego elementu do korzenia, szukamy od najniższego elementu sprawdzając warunek aby lewa dziecka mieściła się pomiędzy lewą i prawą rodzica. Jest to zapytanie szybkie i uniwersalne w przeciwieństwie do zapytanie z metody pierwszej. Np. aby wyszukać drogę od elementu K do korzenia wykonujemy zapytanie: SELECT rodzic.nazwa FROM Kategorie AS dziecko, Kategorie AS rodzic WHERE (dziecko.lewa BETWEEN rodzic.lewa AND rodzic.prawa) AND dziecko.nazwa = 'K' ORDER BY rodzic.lewa;

Wynik zapytania ukazany w tabeli oraz na rysunku: nazwa A D H K 6. Wyświetlenie całego drzewa wraz z zagłębieniem Metoda ta pozwala także na zliczenie zagłębienia dla każdego elementu. Cechy tej nie posiadała poprzednia metoda. Aby pobrać wszystkie elementy wraz z zagłębieniem, wykonujemy zapytanie: SELECT dziecko.nazwa, (COUNT(rodzic.nazwa) - 1) AS zaglebienie FROM Kategorie AS dziecko, Kategorie AS rodzic WHERE (dziecko.lewa BETWEEN rodzic.lewa AND rodzic.prawa) GROUP BY dziecko.nazwa ORDER BY dziecko.lewa;

Wynik zapytania ukazany w tabeli oraz na rysunku: -------------+ nazwa zaglebienie -------------+ A 0 B 1 E 2 C 1 F 2 G 2 D 1 H 2 J 3 K 3 L 3 I 2 -------------+ 7. Wyświetlenie sformatowanego drzewa Używają wbudowanych funkcji MySQL tj. CONCAT łączącą dwa łańcuchy znaków oraz REPEAT łączącą łańcuch znaków określoną ilość razy możemy pobrać całe drzewo z tabeli i wyświetlić je w sformatowany sposób. Wykonujemy zapytanie: SELECT CONCAT(REPEAT(' ',COUNT(rodzic.nazwa)-1), dziecko.nazwa) AS nazwa FROM Kategorie AS dziecko, Kategorie AS rodzic WHERE dziecko.lewa BETWEEN rodzic.lewa AND rodzic.prawa GROUP BY dziecko.nazwa ORDER BY dziecko.lewa;

Otrzymujemy: nazwa A B E C F G D H J K L I 8. Dodanie nowej kategorii poziom niżej Dodanie nowego elementu jest bardziej skomplikowane niż w pierwszej metodzie. Aby dodać element poziom niżej musimy prenumerować całe drzewo dla lewej i prawej strony większej od lewej elementu poniżej którego dodajemy. Na czas zmiany najlepiej jest zablokować tabelę do zapisu aby nikt poza mani nie miał do niej dostępu. Lewa nowego elementu to lewa rodzica + 1 a prawa to lewa rodzica + 2. Np. aby dodać nowy element o nazwie M poniżej elementu F należy wykonać zestaw zapytań: LOCK TABLE Kategorie WRITE; SELECT @lewa := lewa FROM Kategorie WHERE nazwa = 'F'; UPDATE Kategorie SET prawa = prawa + 2 WHERE prawa > @lewa; UPDATE Kategorie SET lewa = lewa + 2 WHERE lewa > @lewa; INSERT INTO Kategorie (nazwa, lewa, prawa) VALUES ('M', @lewa + 1, @lewa + 2); UNLOCK TABLES;

Rysunek wskazuje, gdzie znajduj się nowy element. 9. Dodanie nowej kategorii na tym samym poziomie Dodawanie na tym samym poziomie różni się przenumerowaniem całego drzewa dla elementów gdzie prawa i lewa są większe od prawej ostatniego brata. Lewa i prawa nowego elementu wynoszą odpowiednio: prawa brata + 1 i praw brata + 2. Dodanie elementu N obok L : LOCK TABLE Kategorie WRITE; SELECT @prawa := prawa FROM Kategorie WHERE nazwa = 'L'; UPDATE Kategorie SET prawa = prawa + 2 WHERE prawa > @prawa; UPDATE Kategorie SET lewa = lewa + 2 WHERE lewa > @prawa; INSERT INTO Kategorie (nazwa, lewa, prawa) VALUES ('N', @prawa + 1, @prawa + 2); UNLOCK TABLES;

Zmiany ukazane zostały na rysunku: Aby wyświetlić zmodyfikowane drzewo używany zapytania omawianego wcześniej, ponieważ jest ono uniwersalne i nie trzeba go modyfikować. SELECT CONCAT(REPEAT(' ',COUNT(rodzic.nazwa)-1), dziecko.nazwa) AS nazwa FROM Kategorie AS dziecko, Kategorie AS rodzic WHERE dziecko.lewa BETWEEN rodzic.lewa AND rodzic.prawa GROUP BY dziecko.nazwa ORDER BY dziecko.lewa; Wynik zapytania ukazuje tabela oraz rysunek:

Wnioski Bibliografia: 1. http://www.eioba.pl/a130/drzewa_w_php_i_mysql 2. http://blog.mwojcik.pl/2008/02/17/drzewa-kategorii-w-sql-i-php-metoda-ip/ 3. http://dev.mysql.com/tech-resources/articles/hierarchical-data.html 4. http://www.depesz.com/various/various-sqltrees.php