限制递归查询(with 语句)的初始(锚点)值

Limit the initial (anchor) values of a recursive query (with statement)

假设我们通过树的邻接关系table递归查询获取某个树节点的子节点,但只获取单个子树.

例如,我们将树的邻接关系 table 创建为

CREATE TABLE Tree
       (parent INTEGER,
        child  INTEGER);

INSERT INTO Tree
       VALUES -- (parent -> child)
              (1, 2), (1, 3), (1, 4),
              (2, 5), (2, 11), (3, 9),
              (5, 6), (5, 7),  (5, 8),
              (9, 10), (11, 12);

然后递归查询得到节点2的子节点:

WITH RECURSIVE children_i (parent, child)
AS (
   -- anchor/initial values
   VALUES (NULL, 2)
   -- SELECT parent, child FROM Tree WHERE parent = 2 LIMIT 1
   UNION ALL
   -- recursion
   SELECT children_i.child, Tree.child FROM Tree, children_i
   WHERE Tree.parent = children_i.child
   )
SELECT * FROM children_i;

这将产生

|2
2|5
2|11
5|6
5|7
5|8
11|12

现在我们如何将上面的查询限制为仅遵循 单个 子树(比如仅 2->5->{6, 7, 8} 而不是 2 ->11)? 我试图将 LIMIT 添加到递归的锚点部分,

WITH RECURSIVE children_i (parent, child)
AS (
   -- anchor/initial values
   SELECT parent, child FROM Tree WHERE parent = 2 LIMIT 1
   UNION ALL
   -- recursion
   SELECT children_i.child, Tree.child FROM Tree, children_i
   WHERE Tree.parent = children_i.child
   )
SELECT * FROM children_i;

但它会产生语法错误,LIMIT clause should come after UNION ALL not before (SQLite 3.16.2)。

如何在 SQLite 中实现这一目标?

你似乎想要的不是路径,而是一棵子树,根节点(仅)是超级树根的 children 之一...

您可以通过使用 NOT EXISTS 和子查询选择该根的所有 children 来限制超级树根的 child,它们在数值方面较小。这样只选择children中最小值的child。

WITH RECURSIVE children_i
               (parent,
                child)
AS (
-- anchor/initial values
SELECT t1.parent,
       t1.child
       FROM tree t1
       WHERE t1.parent = 2
             AND NOT EXISTS (SELECT *
                                    FROM tree t2
                                    WHERE t2.parent = t1.parent
                                          AND t2.child < t1.child)
UNION ALL
-- recursion
SELECT c1.child,
       t1.child
       FROM tree t1
            INNER JOIN children_i c1
                       ON t1.parent = c1.child)
SELECT *
       FROM children_i;

db<>fiddle

您可以使用 LIMIT 但您需要将其提取出来以分隔 cte:

WITH anchor AS (
  SELECT parent, child
  FROM tree
  WHERE parent = 2
  -- ORDER BY ...
  LIMIT 1
), children_i(parent,child) AS (
  -- anchor/initial values
  SELECT parent, child
  FROM anchor
  UNION ALL
  -- recursion
  SELECT c1.child, t1.child
  FROM tree t1
  JOIN children_i c1
    ON t1.parent = c1.child
)
SELECT * FROM children_i;

db<>fiddle demo