从 SQLite DB 树表示中选择下一个(同级)和上一个(同级)

发布于 2024-10-09 07:38:26 字数 1037 浏览 0 评论 0原文

我在 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

我可以获得下一个兄弟姐妹和上一个兄弟姐妹,但我坚持下一个和上一个:

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 子句,但我不知道如何实现这一点。我应该提到的是,改变数据库结构不是一个选择。

预先感谢您的任何帮助。

I've got the following sample tree structure in an SQLite DB:

id | idparent | order
1    -1         0
2     1         0
3     2         0
4     1         1

Which represents the following tree:

1
|- 2
|  |- 3
|- 4

And I would like to create a select statement to get all the nodes next, previous, next sibling and previous sibling. Such a statement would return from the previous sample:

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

I can get the nextsibling and previoussiblingbut I'm stuck with the next and previous:

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;

I guess I need to use the WHERE NOT EXISTS clause but I can't figure out how I can achieve that. I should mention that changing the DB structure is not an option.

Thanks in advance for any help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

黑色毁心梦 2024-10-16 07:38:26

您的模式(所谓的“邻接列表模型”)在它支持的操作方面相当有限。相反,尝试嵌套集模式:存储每个节点的边界比每个节点的父节点。节点是父节点边界包含子节点边界的所有节点的后代。界限还给出了树的深度优先遍历,其中下界给出了节点进入的时间,上限给出了节点退出的时间。因此,按左边界对节点进行排序给出了前序遍历,按右边界排序给出了后序遍历。

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)
;

Your schema (what's called the "adjacency list model") is rather limited in terms of which operations it supports. Instead, try the nested set mode: store bounds for each node rather than each node's parent. A node descends from all nodes where the parent's bounds contain the child's. The bounds also give the depth first traversal of the tree, where the lower bound gives when the node is entered and the upper bound gives when the node is exited. Sorting the nodes by the left bound thus gives a pre-order traversal, and sorting by the right gives a post-order traversal.

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`))
;

Result:

+------+------+------+------+------+-------------+-------------+
| 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 |
+------+------+------+------+------+-------------+-------------+

If you really need to find a node's parent (or depth), you can use a view, or use the technique applied in the view to a query:

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)
;
你穿错了嫁妆 2024-10-16 07:38:26

我认为您的架构不支持 next 查询。 IIUC,您可能需要上升多个级别才能确定下一个节点。

我建议添加一个 path 列,它将冒号分隔的路径作为值,例如 1:2:31:4。下一个节点将是 path 顺序中的下一个节点。

I don't think your schema supports a next query. IIUC, you might need to go up multiple levels to determine the next node.

I recommend to add a path column, which takes colon-separated paths as values, such as 1:2:3 or 1:4. The next node will then be the next one in path order.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文