Question

I am using a recursive with statement to select all child from a given parent in a table representing tree structured entries. This is in Sqlite (which now supports recursive with).
This allows me to select very quickly thousands of record in this tree whithout suffering the huge performance loss due to preparing thousands of select statements from the calling application.

WITH RECURSIVE q(Id) AS
(
    SELECT Id FROM Entity
    WHERE Parent=(?)
    UNION ALL
    SELECT m.Id FROM Entity AS m
    JOIN Entity ON m.Id=q.Parent
)
SELECT Id FROM q;

Now, suppose I have related data to these entities in an arbitrary number of other tables, that I want to subsequently load. Due to the arbitrary number of them (in a modular fashion) it is not possible to include the data fetching directly in this one. They must follow it.

But, if for each related tables I then do a SELECT statement, all the performance gain from selecting all the data from the tree directly inside Sqlite is almost useless because I will still stall on thousands of subsequent requests which will each prepare and issue a select statement.

So two questions :

  1. The better solution is to formulate a similar recursive statement for each of the related tables, that will recursively gather the entities from this tree again, and this time select their related data by joining it. This sounds really more efficient, but it's really tricky to formulate such a statement and I'm a bit lost here.

  2. Now the real mystery is, would there be an even more efficient solution, which would be to somehow keep these results from the last query cached somewhere (the rows with the ids from the entity tree) and join them to the related tables in the following statement without having to recursively iterate over it again ?

Here is a try at the first option, supposing I want to select a field Data from related table Component : is the second UNION ALL legal ?

WITH RECURSIVE q(Data) AS
(
    SELECT Id FROM Entity
    WHERE Parent=(?)
    UNION ALL
    SELECT m.Id FROM Entity AS m
    JOIN Entity ON m.Id=q.Parent
    UNION ALL
    SELECT Data FROM Component AS c
    JOIN Component ON c.Id=q.Id
)
SELECT Data FROM q;
Was it helpful?

Solution

The documentation says:

 2. The table named on the left-hand side of the AS keyword must appear exactly once in the FROM clause of the right-most SELECT statement of the compound select, and nowhere else.

So your second query is not legal.

However, the CTE behaves like a normal table/view, so you can just join it to the related table:

WITH RECURSIVE q(Id) AS
( ... )
SELECT q.Id, c.Data
FROM q JOIN Component AS c ON q.Id = c.Id

If you want to reuse the computed values in q for multiple queries, there's nothing you can do with CTEs, but you can store them in a temporary table:

CREATE TEMPORARY TABLE q_123 AS
WITH RECURSIVE q(Id) AS
( ... )
SELECT Id FROM q;

SELECT * FROM q_123 JOIN Component ...;
SELECT * FROM q_123 JOIN Whatever ...;

DROP TABLE q_123;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top