-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnested_set.sql
154 lines (144 loc) · 4.6 KB
/
nested_set.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
-- main table
CREATE TABLE tree (
_id INTEGER,
name TEXT NOT NULL,
lft INTEGER,
rgt INTEGER,
parent INTEGER,
PRIMARY KEY(_id)
)
-- helper table to store variable
CREATE TABLE p (
_id INTEGER,
rgt INTEGER,
lft INTEGER,
PRIMARY KEY(_id)
)
-- init table
INSERT INTO tree (_id, name,lft,rgt) VALUES (1, 'root',1,2);
-- trigger to insert, move or delete item/subtree
-- to insert new item: fields name and parent are required
CREATE TRIGGER insert_item AFTER INSERT ON tree
BEGIN
UPDATE tree SET lft=(SELECT rgt FROM tree WHERE _id=New.parent),
rgt=(SELECT rgt FROM tree WHERE _id=New.parent)+1
WHERE _id IS NEW._id;
UPDATE tree SET lft=lft+2 WHERE lft > (SELECT rgt FROM tree WHERE _id=NEW.parent) AND _id IS NOT NEW._id;
UPDATE tree SET rgt=rgt+2 WHERE rgt >= (SELECT rgt FROM tree WHERE _id=NEW.parent) AND _id IS NOT NEW._id;
END
-- after item is removed: recalculating the tree
CREATE TRIGGER delete_item AFTER DELETE ON tree
BEGIN
DELETE FROM tree WHERE lft BETWEEN OLD.lft AND OLD.rgt;
UPDATE tree SET lft=lft-round((OLD.rgt-OLD.lft+1)) WHERE lft > OLD.rgt;
UPDATE tree SET rgt=rgt-round((OLD.rgt-OLD.lft+1)) WHERE rgt > OLD.rgt;
END
-- to move item/subtree: new parent id is required
CREATE TRIGGER update_item BEFORE UPDATE OF parent ON tree
BEGIN
INSERT OR REPLACE INTO p (_id, lft) VALUES (1, (SELECT lft FROM tree WHERE _id=NEW.parent));
UPDATE tree
SET
lft = lft + CASE
WHEN (SELECT lft FROM p WHERE _id=1) < OLD.lft THEN CASE
WHEN lft >= OLD.rgt +1 THEN 0
ELSE CASE
WHEN lft >= OLD.lft THEN (SELECT lft FROM p WHERE _id=1) - OLD.lft +1
ELSE CASE
WHEN lft >= (SELECT lft FROM p WHERE _id=1) +1 THEN OLD.rgt - OLD.lft +1
ELSE 0
END
END
END
ELSE CASE
WHEN lft >= (SELECT lft FROM p WHERE _id=1) +1 THEN 0
ELSE CASE
WHEN lft >= OLD.rgt +1 THEN -OLD.rgt + OLD.lft -1
ELSE CASE
WHEN lft >= OLD.lft THEN (SELECT lft FROM p WHERE _id=1) - OLD.rgt
ELSE 0
END
END
END
END,
rgt = rgt + CASE
WHEN (SELECT lft FROM p WHERE _id=1) < OLD.lft THEN CASE
WHEN rgt >= OLD.rgt +1 THEN 0
ELSE CASE
WHEN rgt >= OLD.lft THEN (SELECT lft FROM p WHERE _id=1) - OLD.lft +1
ELSE CASE
WHEN rgt >= (SELECT lft FROM p WHERE _id=1) +1 THEN OLD.rgt - OLD.lft +1
ELSE 0
END
END
END
ELSE CASE
WHEN rgt >= (SELECT lft FROM p WHERE _id=1) +1 THEN 0
ELSE CASE
WHEN rgt >= OLD.rgt +1 THEN -OLD.rgt + OLD.lft -1
ELSE CASE
WHEN rgt >= OLD.lft THEN (SELECT lft FROM p WHERE _id=1) - OLD.rgt
ELSE 0
END
END
END
END
WHERE (SELECT lft FROM p WHERE _id=1) < OLD.lft OR (SELECT lft FROM p WHERE _id=1) > OLD.rgt;
END
-- queries
-- all elements with level
SELECT n.name,
COUNT(*)-1 AS level
FROM tree AS n,
tree AS p
WHERE n.lft BETWEEN p.lft AND p.rgt
GROUP BY n.lft
ORDER BY n.lft;
-- all elements with level and children count
SELECT n.name,
COUNT(*)-1 AS level,
ROUND ((n.rgt - n.lft - 1) / 2) AS offspring
FROM tree AS n,
tree AS p
WHERE n.lft BETWEEN p.lft AND p.rgt
GROUP BY n.lft
ORDER BY n.lft;
-- all elements level 1
SELECT n.name,
COUNT(*)-1 AS level
FROM tree AS n,
tree AS p
WHERE n.lft BETWEEN p.lft AND p.rgt
GROUP BY n.lft
HAVING level =1
ORDER BY n.lft;
-- subtree(1)
SELECT n.name,
COUNT(*)-1 AS level
FROM tree AS n,
(SELECT lft, rgt FROM tree WHERE _id=$ID) AS p
WHERE n.lft BETWEEN p.lft AND p.rgt
GROUP BY n.lft
ORDER BY n.lft;
-- subtree(2)
SELECT node.name, (COUNT(parent._id) - (sub_tree.level + 1)) AS depth
FROM tree AS node,
tree AS parent,
tree AS sub_parent,
(
SELECT node.name, (COUNT(parent._id) - 1) AS level
FROM tree AS node,
tree AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node._id=$ID
GROUP BY node._id
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node._id
HAVING depth = 1 --WITHOUT ROOT
--HAVING depth <= 1 --WITH ROOT
--HAVING depth >= 1 --WITH CHILDREN
ORDER BY node.lft;