Предположим, что у нас есть дерево с помеченными узлами, где каждый узел имеет уникальный идентификатор и неуникальную метку. Путь в дереве можно описать упорядоченным набором меток; например, с дескриптором пути P = ['', 'a', 'a.1', 'a.1.3'] = "/a/a.1/a.1.3"
(аналогично пути в Unix). Цель состоит в том, чтобы найти идентификаторы узлов, которые соответствуют этому пути; например, P = 1 -> 2 -> 5 -> 8
.
Сделаем минимальный пример на SQLite следующим образом:
-- define the nodes
CREATE TABLE nd
(uid INTEGER PRIMARY KEY,
label TEXT);
-- note that some nodes might have similar labels (here, 2 and 9)
INSERT INTO nd
VALUES (1, '' ), (4, 'd'),
(3, 'b'), (9, 'a'),
(2, 'a'), (5, 'a.1'),
(6, 'a.1.1'), (7, 'a.1.2'), (8, 'a.1.3');
-- define the tree structure (adjacency list)
CREATE TABLE tr
(child INTEGER REFERENCES nd (uid),
parent INTEGER REFERENCES nd (uid));
INSERT INTO tr
VALUES (1, NULL), (4, 1),
(3, 1), (9, 3),
(2, 1), (5, 2),
(6, 5), (7, 5), (8, 5);
/* Tree structure (+ indicates a node)
+ 1:root
|
---------------
| | |
+ 4:d + 3:b + 2:a
| |
+ 9:a + 5:a.1
|
---------------
| | |
+ + +
8 7 6
a.1.3 a.1.2 a.1.1
*/
Можно легко найти прямых потомков узла с id = 1:
SELECT tr.child AS uid, nd.label AS label
FROM tr
JOIN nd
ON tr.child = nd.uid
WHERE tr.parent = 1;
Или найдите идентификатор прямого дочернего элемента «b» узла 1:
SELECT tr.child AS uid, nd.label AS label
FROM tr
JOIN nd
ON (tr.parent = 1 AND nd.label = 'b'
AND tr.child = nd.uid);
Теперь давайте определим дескриптор пути P = "/a/a.1/a.1.3"
:
CREATE TABLE P (label TEXT);
INSERT INTO P VALUES
(''), ('a'), ('a.1'), ('a.1.3'); -- path 1/2/5/8
Я составил (или вообразил) рекурсивный запрос, чтобы найти соответствующие uid:
WITH RECURSIVE
trPath (uid, label, depth) AS (
-- first (uid, label) from the path descriptor
SELECT nd.uid, nd.label, 1 FROM nd
WHERE nd.label = (SELECT label FROM P WHERE P.ROWID = 1)
UNION ALL
WITH
-- next uid from path descriptor (only a single element)
parentLabel (uid) AS (
SELECT uid FROM nd
WHERE nd.label = (SELECT P.label FROM P, trPath WHERE P.ROWID = trPath.depth + 1)
LIMIT 1
),
-- uids of children of current parent
childrenIds (uid) AS (
SELECT child FROM tr
WHERE tr.parent = (SELECT uid FROM parentLabel)
),
-- uid, label of children of current parent
children (uid, label) AS (
SELECT chl.uid, nd.label FROM childrenIds
JOIN nd ON (nd.uid = childrenIds.uid)
)
SELECT uid, label, trPath.depth + 1 from children, trPath
)
SELECT * FROM trPath;
В результате должна получиться таблица вида
uid | label | depth
1|''|1
2|a|2
5|a.1|3
8|a.1.3|4
Тем не менее, это не работает, так как это действительно недопустимый запрос SQLite. Как выполнить такой запрос в SQLite?