Recursive SQL Queries with PostgreSQL
towardsdatascience.com
towardsdatascience.com
So instead of
select something from x join y on x.foo = y.foo and blah = 'cheese' where some_complicated_join_conditions ...
union
select something from x join y on x.foo = y.foo and blah <> 'cheese' where -ome_other_complicated_join_conditions
...you can have with nerk as (
select something from x join y on x.foo = y.foo
where
hopefully you can find some common part of the complicated where clauses
)
select something from nerk where blah = 'cheese' and something
union
select something from nerk where blah <> 'cheese' and something_else
...obviously a trivial example but you get the idea. When I tested this on postgres (some time ago) this was very significantly faster for my use case.It also helps in use cases where you want to allow user-defined filtering and ordering logic controlled by a gui. You can put your basic query as the CTE and then have all the user-controlled bits and pieces in the final select and they don't get mixed up together.
I've run into issues before with systems where the result of a CTE is so large that it is actually advantageous to instead place it into a temp table to avoid re-fetching the data at each invocation. Sometimes that can be better than a CTE for this use case - YMMV.
select a.foo, b.bar
from a, b
where a.b_id = b.id
and a.something ='blah'
would be reliably 5-10% faster than select a.foo, b.bar
from a
join b on a.b_id = b.id
where a.something ='blah'
even if everything was indexed etc.Conversely, projecting off unnecessary columns used to result in a big speedup. ie
select a1.foo, b.bar
from
(select foo, b_id from a where something='blah') a1
join b on a1.b_id = b.id
used to be significantly faster than either of those queries on wide tables.edit: few typos and additional example added
Any chance you either had a lot more tables joined together, or you were seeing caching effects?
> Conversely, projecting off unnecessary columns used to result in a big speedup.
That also just gets inlined, unless you have more than from_collapse_limit items in the from list. And we push down the list of columns we actually need, so there's nothing this would improve anyway.
I'm not sure if MySQL does now, but about a decade ago when I did my bachelors, in the database course we switched from doing exercises in MySQL to PostgreSQL when recursion came up. Our local university has a database research group headed by a professor who has a fetish for recursion, so we spent a good chunk of time shoehorning graph and tree problems into SQL. As the story goes, thanks to his constant pestering, DB2 supports quadratic recursion.
I may have misremembered since I did go a bit wild on this query (done while learning about recursive queries), but pulling the subquery into a CTE doesn't affect the query plan.
Limiting to linear recursion makes it less powerful and harder to program than Datalog, but the algorithm is sufficiently fast. Which is probably necessary as the recursive table is not indexed.
Also you're not only allowed UNION but also UNION ALL without a duplicate check. There every new result has to be strictly smaller than the last or you lose termination. Together with the strange referencing method to only the new tuples, this is quite a footgun.
But not to be a spoilsport, we still teach it at our University for some problems that would have required external programs to calculate the fixpoint. You just have to know the limitations which the article does not explore.
WITH RECURSIVE my_cte AS (
SELECT foo, bar FROM whatever
UNION ALL
SELECT (s).*
FROM my_cte, LATERAL (
SELECT ... -- refer to my_cte multiple times here
) s
)Not every of those path queries are easily implemented using recursive CTEs.
sounds dangerous for job security