Reputation: 45
I have a SQLite database with a table that represents a tree. Each row in the table represents a relationship between two nodes except the first node which links to itself.
Basically given this table
BEGIN TRANSACTION;
CREATE TABLE "unnamed" (key TEXT PRIMARY KEY, value TEXT);
INSERT INTO `unnamed` (key,value) VALUES ('1','1');
INSERT INTO `unnamed` (key,value) VALUES ('2','1');
INSERT INTO `unnamed` (key,value) VALUES ('3','10');
INSERT INTO `unnamed` (key,value) VALUES ('10','5');
INSERT INTO `unnamed` (key,value) VALUES ('5','16');
INSERT INTO `unnamed` (key,value) VALUES ('16','8');
INSERT INTO `unnamed` (key,value) VALUES ('8','4');
INSERT INTO `unnamed` (key,value) VALUES ('4','2');
INSERT INTO `unnamed` (key,value) VALUES ('6','3');
INSERT INTO `unnamed` (key,value) VALUES ('7','22');
INSERT INTO `unnamed` (key,value) VALUES ('22','11');
INSERT INTO `unnamed` (key,value) VALUES ('11','34');
INSERT INTO `unnamed` (key,value) VALUES ('34','17');
INSERT INTO `unnamed` (key,value) VALUES ('17','52');
INSERT INTO `unnamed` (key,value) VALUES ('52','26');
INSERT INTO `unnamed` (key,value) VALUES ('26','13');
INSERT INTO `unnamed` (key,value) VALUES ('13','40');
INSERT INTO `unnamed` (key,value) VALUES ('40','20');
INSERT INTO `unnamed` (key,value) VALUES ('20','10');
INSERT INTO `unnamed` (key,value) VALUES ('9','28');
INSERT INTO `unnamed` (key,value) VALUES ('28','14');
INSERT INTO `unnamed` (key,value) VALUES ('14','7');
COMMIT;
Output this table
+------+------------------------------------------------------+
| Node | Path |
+------+------------------------------------------------------+
| 1 | 1 |
| 2 | 2-1 |
| 3 | 3-10-5-16-8-4-2-1 |
| 4 | 4-2-1 |
| 5 | 5-16-8-4-2-1 |
| 6 | 6-3-10-5-16-8-4-2-1 |
| 7 | 7-22-11-34-17-52-26-13-40-20-10-5-16-8-4-2-1 |
| 8 | 8-4-2-1 |
| 9 | 9-28-14-7-22-11-34-17-52-26-13-40-20-10-5-16-8-4-2-1 |
| 10 | 10-5-16-8-4-2-1 |
| 11 | 11-34-17-52-26-13-40-20-10-5-16-8-4-2-1 |
| 13 | 13-40-20-10-5-16-8-4-2-1 |
| 14 | 14-7-22-11-34-17-52-26-13-40-20-10-5-16-8-4-2-1 |
| 16 | 16-8-4-2-1 |
| 17 | 17-52-26-13-40-20-10-5-16-8-4-2-1 |
| 20 | 20-10-5-16-8-4-2-1 |
...
I have been reading about WITH
and WITH RECURSIVE
, but I cannot get my head around how they work.
Upvotes: 0
Views: 374
Reputation: 896
This solution constructs the path from leaf to root:
WITH RECURSIVE
queue(leaf,head,path) AS (
SELECT CAST(key AS INTEGER) AS leaf, key AS head, key AS path
FROM unnamed
UNION
SELECT queue.leaf AS leaf, unnamed.value AS head, (queue.path||'-'||unnamed.value) AS path
FROM unnamed, queue
WHERE unnamed.value != queue.head AND unnamed.key = queue.head
)
SELECT leaf AS node, path AS path
FROM queue
WHERE head = 1
-- WHERE length(path) = (SELECT MAX(LENGTH(path)) FROM queue AS q WHERE q.leaf = queue.leaf)
ORDER BY leaf;
Please read the documentation here: http://www.sqlite.org/lang_with.html
The initial-select adds all keys into the queue as leaf and head and path. Leaf is cast to integer for the ORDER BY later on, head is the current head and path will be extended step by step:
SELECT CAST(key AS INTEGER) AS leaf, key AS head, key AS path
FROM unnamed
For each queue item the database is queried to extend the path at the head towards the root of the tree. So the head of the queue item has to match a key, which is not the root of the tree.
WHERE unnamed.value != queue.head AND unnamed.key = queue.head
The combined path of the two is extended with a dash and added to the queue:
SELECT queue.leaf AS leaf, unnamed.value AS head, (queue.path||'-'||unnamed.value) AS path
FROM unnamed, queue
WHERE unnamed.value != queue.head AND unnamed.key = queue.head
The result contains all paths from leaf to root including all intermediate results. Therefore we only select those, which ended at the root node with head/key value 1.
SELECT leaf AS node, path AS path
FROM queue
WHERE head = 1
ORDER BY leaf;
Alternatively you can also just select those with the longest path.
SELECT leaf AS node, path AS path
FROM queue
WHERE length(path) = (SELECT MAX(LENGTH(path)) FROM queue AS q WHERE q.leaf = queue.leaf)
ORDER BY leaf;
Upvotes: 1