Search code examples
sqlitelimitrecursive-querywith-statementsubtree

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


Suppose we perform a recursive query to obtain the children of a certain tree node via an adjacency table of the tree, but it suffices to obtain only a single sub-tree.

As an example, let's create the adjacency table of the tree as

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

and then make a recursive query to obtain the children of node 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;

which will produce

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

Now how could we restrict the query above to follow only a single sub-tree (say only 2->5->{6, 7, 8} and not 2->11)? I have tried to add a LIMIT to the anchor part of the recursion,

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;

yet it yields a syntax error, LIMIT clause should come after UNION ALL not before (SQLite 3.16.2).

How could one achieve this in SQLite?


Solution

  • You could use LIMIT but you need to extract it to separate 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