我在SQLite DB中有以下示例树结构:
id | idparent | order
1 -1 0
2 1 0
3 2 0
4 1 1
代表以下树:
1
|- 2
| |- 3
|- 4
我想创建一个select语句来获取下一个,前一个,下一个兄弟节点和上一个兄弟节点的所有节点。这样的陈述将从前一个样本返回:
id | idparent | next | previous | nextsibling | previoussibling
1 -1 2 NULL NULL NULL
2 1 3 1 4 NULL
3 2 4 2 NULL NULL
4 1 NULL 3 NULL 2
我可以得到nextsibling和previoussibling但我仍然坚持下一个和上一个:
SELECT node.id, node.idparent,
??? AS next
??? AS previous,
(SELECT id FROM nodes WHERE idparent = node.idparent AND `order` > node.`order` LIMIT 1) AS nextsibbling,
(SELECT id FROM nodes WHERE idparent = node.idparent AND `order` < node.`order` LIMIT 1) AS previoussibbling
FROM nodes node;
我想我需要使用WHERE NOT EXISTS子句,但我无法弄清楚如何实现这一点。我应该提一下,改变数据库结构不是一种选择。
提前感谢您的帮助。
答案 0 :(得分:2)
您的架构(所谓的“邻接列表模型”)在其支持的操作方面相当有限。相反,尝试每个节点的nested set mode:存储边界而不是每个节点的父节点。节点从父节点包含子节点的所有节点下降。边界还给出了树的深度优先遍历,其中下限在输入节点时给出,上限在节点退出时给出。按左边界排序节点因此给出了预先遍序的遍历,右边的排序给出了一个后序遍历。
CREATE TABLE `hier` (
`id` int(11) PRIMARY KEY AUTO_INCREMENT,
`left` int(11) NOT NULL,
`right` int(11) NOT NULL,
`data` varchar(128),
INDEX `bounds` (`left`,`right`),
INDEX `sdnuob` (`right`, `left`)
);
INSERT INTO HIER (id, `left`, `right`, data)
VALUES
(1, 1, 8, 'foo'),
(2, 2, 5, 'mane'),
(3, 3, 4, 'padme'),
(4, 6, 7, 'hum')
;
SELECT h.id AS node,
p.id AS prev, p.`left` AS p_l, n.id AS `next`, n.`left` AS n_l,
ps.id AS prevSibling, ns.id AS nextSibling
FROM hier AS h
LEFT JOIN hier AS p ON h.`left` > p.`left`
LEFT JOIN hier AS pb ON h.`left` > pb.`left`
LEFT JOIN hier AS n ON h.`left`< n.`left`
LEFT JOIN hier AS nb ON h.`left`< nb.`left`
LEFT JOIN hier AS ps ON h.`left` = ps.`right`+1
LEFT JOIN hier AS ns ON h.`right`= ns.`left`-1
GROUP BY node, prevSibling, nextSibling, p.`left`, n.`left`
HAVING (p.`left` IS NULL OR p.`left` = MAX(pb.`left`))
AND (n.`left` IS NULL OR n.`left` = MIN(nb.`left`))
;
结果:
+------+------+------+------+------+-------------+-------------+ | node | prev | p_l | next | n_l | prevSibling | nextSibling | +------+------+------+------+------+-------------+-------------+ | 1 | NULL | NULL | 2 | 2 | NULL | NULL | | 2 | 1 | 1 | 3 | 3 | NULL | 4 | | 3 | 2 | 2 | 4 | 6 | NULL | NULL | | 4 | 3 | 3 | NULL | NULL | 2 | NULL | +------+------+------+------+------+-------------+-------------+
如果您确实需要查找节点的父级(或深度),则可以使用视图,或使用视图中应用的技术查询:
CREATE VIEW hier_depth AS
SELECT c.*, p.id AS parent, p.`left` AS p_left, COUNT(a.id) AS depth
FROM hier AS c
LEFT JOIN hier AS p ON p.`left` < c.`left` AND c.`right` < p.`right`
LEFT JOIN hier AS a ON a.`left` < c.`left` AND c.`right` < a.`right`
GROUP BY c.id, parent
HAVING p.`left` IS NULL OR p.`left` = MAX(a.left)
;
答案 1 :(得分:1)
我认为您的架构不支持next
查询。 IIUC,您可能需要上升多个级别来确定下一个节点。
我建议添加path
列,该列以冒号分隔的路径作为值,例如1:2:3
或1:4
。然后,下一个节点将成为path
订单中的下一个节点。