查询树中给定记录上方的节点

时间:2019-01-26 11:40:21

标签: sql postgresql

我有桌子

               Table "public.menu_items"
   Column    |  Type   | Collation | Nullable | Default 
-------------+---------+-----------+----------+---------
 id          | integer |           | not null | 
 name        | text    |           |          | 
 parent_id   | integer |           |          | 
 position    | integer |           |          | 
 is_category | boolean |           |          | 

我有这个记录树

Lesson1 (id: 1, parent_id: null, position: 1, is_category: false)
Category2 (id: 2, parent_id: null, position: 2, is_category: true)
|_ Lesson2.1 (id: 5, parent_id: 2, position: 1, is_category: false)
|_ Category2.2 (id: 6, parent_id: 2, position: 2, is_category: true)
   |_ Lesson2.2.1 (id: 8, parent_id: 6, position: 1, is_category: false)
   |_ Lesson2.2.2 (id: 9, parent_id: 6, position: 2, is_category: false)
   |_ Lesson2.2.3 (id: 10, parent_id: 6, position: 3, is_category: false)
   |_ Lesson2.2.4 (id: 11, parent_id: 6, position: 4, is_category: false)
|_ (Lesson2.3 (id: 7, parent_id: 2, position: 3, is_category: false)
Lesson3 (id: 3, parent_id: null, position: 3, is_category: false)
Category4 (id: 4, parent_id: null, position: 4, is_category: true)

我知道idposition的记录,例如id = 10,位置=3。(课程2.2.3)

我需要查询树中给定记录上方的所有课程(is_category = false)。就我们而言

Lesson1 (id: 1, parent_id: null, position: 1, is_category: false)
Lesson2.1 (id: 5, parent_id: 2, position: 1, is_category: false)
Lesson2.2.1 (id: 8, parent_id: 6, position: 1, is_category: false)
Lesson2.2.2 (id: 9, parent_id: 6, position: 2, is_category: false)

为此,我可能需要使用WITH RECURSIVE。如何编写这样的查询?

更新 现在我的查询看起来像这样,但是它只获取祖先,我可能需要嵌套WITH RECURSIVE来获取每个祖先的子代。

WITH RECURSIVE r AS
  (SELECT *
   FROM menu_items
   WHERE parent_id =
       (SELECT parent_id
        FROM menu_items
        WHERE id = 10)
     AND POSITION < 3
   UNION SELECT x.*
   FROM menu_items x
   JOIN r ON x.id = r.parent_id
   OR (x.parent_id IS NULL
       AND r.parent_id IS NULL
       AND x.position <
         (SELECT POSITION
          FROM menu_items
          WHERE id = r.id)))
SELECT *
FROM r;

1 个答案:

答案 0 :(得分:1)

免责声明:我不确定我是否完全了解了您的用例(请参阅问题下方的注释)。但是总的来说,这是一个解决方案。也许您应该校准一些小东西...


demo:db<>fiddle

WITH RECURSIVE rec_lessons AS (
    SELECT 
        id,
        position,
        name,
        parent_id,
        position::text as path,
        is_category
    FROM
        lessons
    WHERE parent_id IS NULL

    UNION ALL

    SELECT 
        l.id,
        l.position,
        l.name,
        l.parent_id,
        rl.path || '.' || l.position,
        l.is_category
    FROM
        lessons l
    JOIN rec_lessons rl ON l.parent_id = rl.id
)
SELECT * 
FROM rec_lessons
WHERE is_category = false

递归CTE始终由两部分组成,并由UNION子句分隔。第一个查询是起点,第二个查询是递归部分。

首先,我们需要找到可能是您的树根的所有行。我以为这就是没有父母的全部。

对于递归,我们查询原始表中所有具有上一回合的id作为parent_id的行。这就是联接的作用。因此,一开始所有根都具有ids (1,2,3,4)。在第一个递归中,我们找到parent_ids(1,2,3,4)之一的所有行。这样我们得到了带有ids (5,6,7)的行。这些行与UNION ALL合并为起始结果。下一步是找到所有带有parent_ids的行都是(5,6,7)之一,依此类推。

我添加了可以描述树的路径。这在每个角落都得到扩展。

最后(在WITH子句之外),我过滤了所有非类别。