从SQLite DB树表示中选择next(兄弟)和previous(兄弟)

时间:2010-12-27 16:10:36

标签: sql database sqlite data-structures tree

我在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子句,但我无法弄清楚如何实现这一点。我应该提一下,改变数据库结构不是一种选择。

提前感谢您的帮助。

2 个答案:

答案 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:31:4。然后,下一个节点将成为path订单中的下一个节点。