Hierarchiczna / drzewiasta baza danych dla ścieżek katalogów w systemie plików

Chcę przechowywać katalogi (obecne na dysku) w bazie danych, zachowując ich hierarchiczną / drzewiastą strukturę.

Oto rysunek:

                         (ROOT)
                       /        \ 
                    Dir2        Dir3
                   /    \           \
                 Dir4   Dir5        Dir6
                 /          
               Dir7

Korzystam z bazy danych SQLite.

Zaproponuj Mi:

  1. Zapytanie SQL do przechowywania powyższej struktury w bazie danych SQLite oraz

  2. Zapytanie do pobrania pełnej ścieżki katalogu po wybraniu jednego.

    Tzn. Załóżmy, że wybieram {[1] } wtedy powinienem pełna ścieżka jak ROOT/Dir2/Dir4/Dir7

Author: Mariano Desanze, 2011-07-23

3 answers

Oto przykład tabeli szybkiego zamykania dla SQLite. Nie uwzględniłem instrukcji wstawiania elementów do istniejącego drzewa. Zamiast tego, po prostu stworzyłem instrukcje ręcznie. Instrukcje insert I delete można znaleźć w modelach dla danych hierarchicznych .

Ze względu na moje zdrowie psychiczne podczas wstawiania identyfikatorów dla katalogów, zmieniłem nazwy katalogów, aby pasowały do ich identyfikatorów:

        (ROOT)
      /        \ 
    Dir2        Dir3
    /    \           \
  Dir4   Dir5        Dir6
  /          
Dir7

Tworzenie tabel

CREATE TABLE `filesystem` (
  `id` INTEGER,
  `dirname` TEXT,
  PRIMARY KEY (`id`)
);

CREATE TABLE `tree_path` (
  `ancestor` INTEGER,
  `descendant` INTEGER,
  PRIMARY KEY (`ancestor`, `descendant`)
);

Insert katalogi do filesystem Tabela

INSERT INTO filesystem (id, dirname) VALUES (1, 'ROOT');
INSERT INTO filesystem (id, dirname) VALUES (2, 'Dir2');
INSERT INTO filesystem (id, dirname) VALUES (3, 'Dir3');
INSERT INTO filesystem (id, dirname) VALUES (4, 'Dir4');
INSERT INTO filesystem (id, dirname) VALUES (5, 'Dir5');
INSERT INTO filesystem (id, dirname) VALUES (6, 'Dir6');
INSERT INTO filesystem (id, dirname) VALUES (7, 'Dir7');

Utwórz ścieżki tabeli zamknięcia

INSERT INTO tree_path (ancestor, descendant) VALUES (1, 1);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 2);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 3);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 4);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 5);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 6);
INSERT INTO tree_path (ancestor, descendant) VALUES (1, 7);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 2);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 4);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 5);
INSERT INTO tree_path (ancestor, descendant) VALUES (2, 7);
INSERT INTO tree_path (ancestor, descendant) VALUES (3, 3);
INSERT INTO tree_path (ancestor, descendant) VALUES (3, 6);
INSERT INTO tree_path (ancestor, descendant) VALUES (4, 4);
INSERT INTO tree_path (ancestor, descendant) VALUES (4, 7);
INSERT INTO tree_path (ancestor, descendant) VALUES (5, 5);
INSERT INTO tree_path (ancestor, descendant) VALUES (6, 6);
INSERT INTO tree_path (ancestor, descendant) VALUES (7, 7);

Uruchom kilka zapytań

# (ROOT) and subdirectories
SELECT f.id, f.dirname FROM filesystem f
  JOIN tree_path t
    ON t.descendant = f.id
 WHERE t.ancestor = 1;

+----+---------+
| id | dirname |
+----+---------+
|  1 | ROOT    |
|  2 | Dir2    |
|  3 | Dir3    |
|  4 | Dir4    |
|  5 | Dir5    |
|  6 | Dir6    |
|  7 | Dir7    |
+----+---------+


# Dir3 and subdirectories
SELECT f.id, f.dirname
  FROM filesystem f
  JOIN tree_path t
    ON t.descendant = f.id
 WHERE t.ancestor = 3;

+----+---------+
| id | dirname |
+----+---------+
|  3 | Dir3    |
|  6 | Dir6    |
+----+---------+

# Dir5 and parent directories
SELECT f.id, f.dirname
  FROM filesystem f
  JOIN tree_path t
    ON t.ancestor = f.id
 WHERE t.descendant = 5;

+----+---------+
| id | dirname |
+----+---------+
|  1 | ROOT    |
|  2 | Dir2    |
|  5 | Dir5    |
+----+---------+

# Dir7 and parent directories
SELECT f.id, f.dirname
  FROM filesystem f
  JOIN tree_path t
    ON t.ancestor = f.id
 WHERE t.descendant = 7;

+----+---------+
| id | dirname |
+----+---------+
|  1 | ROOT    |
|  2 | Dir2    |
|  4 | Dir4    |
|  7 | Dir7    |
+----+---------+

SELECT f.id, f.dirname
  FROM filesystem f
  JOIN tree_path t
    ON t.ancestor = f.id
 WHERE t.descendant = (
SELECT id
  FROM filesystem
 WHERE dirname LIKE '%7%'
);

+----+---------+
| id | dirname |
+----+---------+
|  1 | ROOT    |
|  2 | Dir2    |
|  4 | Dir4    |
|  7 | Dir7    |
+----+---------+
 41
Author: Mike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-10 11:54:27

Myślę, że powinieneś poczytać o metodzie caled Modified Preorder Tree Traversal: http://www.sitepoint.com/hierarchical-data-database/

Ten link omawia dwie metody przechowywania danych hierarchicznych w relacyjnych bazach danych: Model listy adiacenckiej i zmodyfikowany algorytm trawersowania drzewa preorderowego.

Główną ideą zmodyfikowanej metody Pre-Order Tree Traversal jest adnotacja wszystkich węzłów za pomocą wskaźników wspomagających nawigację i drzewo podrzędne wybór: Tutaj wpisz opis obrazka

 3
Author: Jackson Cassimiro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-09 11:51:35

Reprezentujesz dane hierarchiczne jako serię węzłów, z których każdy ma identyfikator i identyfikator nadrzędny. Możesz zapisać swój W tabeli o nazwie DIRTAB z 2 kolumnami ID i jedną dla tekstu nazwy katalogu:

ID -- as a primary key  
PARENT_ID -- refers to the ID of the parent row in DIRTAB  
DIRNAME -- the text of the name eg Dir5  

SQLite nie ma klauzuli CONNECT BY, że Oracle musi przetwarzać dane hierarchiczne, ale myślę, że jeśli jesteś gotowy zaakceptować jakiś brzydki SQL, możesz zbliżyć coś hierarchicznego:

SELECT (CASE WHEN p5.DIRNAME IS NOT NULL THEN p5.DIRNAME || '/' ELSE '' END) ||
       (CASE WHEN p4.DIRNAME IS NOT NULL THEN p4.DIRNAME || '/' ELSE '' END) ||
       (CASE WHEN p3.DIRNAME IS NOT NULL THEN p3.DIRNAME || '/' ELSE '' END) ||
       (CASE WHEN p2.DIRNAME IS NOT NULL THEN p2.DIRNAME || '/' ELSE '' END) ||
       (CASE WHEN p1.DIRNAME IS NOT NULL THEN p1.DIRNAME || '/' ELSE '' END) ||
       p0.DIRNAME as FULLPATH
FROM DIRTAB p0
     LEFT OUTER JOIN DIRTAB p1 ON p1.ID = p0.PARENT_ID
     LEFT OUTER JOIN DIRTAB p2 ON p2.ID = p1.PARENT_ID
     LEFT OUTER JOIN DIRTAB p3 ON p3.ID = p2.PARENT_ID
     LEFT OUTER JOIN DIRTAB p4 ON p4.ID = p3.PARENT_ID
     LEFT OUTER JOIN DIRTAB p5 ON p5.ID = p4.PARENT_ID
WHERE p0.DIRNAME = 'Dir6'  

Problem polega na tym, że musisz przewidzieć maksymalną głębokość struktury katalogów i rozwiń instrukcję SQL, aby poradzić sobie. Zrobiłem 6 poziomów jako przykład.
Zakładam również, że SQLite nie ma problemu z konkatenacją pustych łańcuchów. (Niektóre DBs traktują je jako null i konwertują cały wynik wyrażenia na null)

 1
Author: Morbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-23 19:42:37