简体   繁体   中英

How oracle recursive WITH clause works under the hood

With common table expression aka with clause, we can name a subquery, and reference this subquery from anywhere in our SQL statement. More interestingly, we can reference the query from the query itself, thus enabling recursion, which makes SQL turing complete language.

The concept is simple, but I'm interested how Oracle technically implements the recursive aspect in the term of call stack, rows and eagerness of evaluation.

Here is simple example of recursive with clause in action. It's mandated recursive with clause consist of two members: the anchor member (initial rows) and the recursive member, combined by union all operator.

with numbers(val) as (
  select 1 as val from dual
  union all
  select val + 1 from numbers
  where val < 5
)
select val from numbers

Which produces

VAL
--
1
2
3
4
5

How does the call stack to accumulate the result set "look like" in this example? Is all the logic how result is built derivable from this statement in the terms of SQL but I just don't get it, or is there some magic how Oracle interpret this statement to implement the recursion?

Also, why the limiting factor ( where clause) has to be in the recursive member itself, and not where the CTE is used? The following attempt raises error

with numbers(val) as (
  select 1 as val from dual
  union all
  select val + 1 from numbers
)
select val from numbers where val < 5

And the error is

ORA-32044: cycle detected while executing recursive WITH query

Does this mean Oracle does not support lazy evaluation with common table expression? In contrast, the following works in PostgreSQL.

WITH RECURSIVE t(v) AS (
  SELECT 1     -- Seed Row
  UNION ALL
  SELECT v + 1 -- Recursion
  FROM t
)
SELECT v
FROM t
LIMIT 5

Which reminds me more of lazily evaluated data with a terminal operation. What is the technical difference here to Oracle?

I must admit I have problems understanding your question. The recursive query is built thus:

<start query>
UNION ALL
<next query>

The <start query> gets executed and retrieves rows. For each of these rows <next query> gets executed, producing more rows. And for each of the newly created rows <next query> gets executed again and so on and so on until the criteria in the WHERE clause is not met anymore.

You create a row with value 1. Then you create a new row with 1+1=2. Then you create a new row with 2+1=3. Then you create a new row with 3+1=4. Then you create a new row with 4+1=5. And there you stop, because 5 does not match the criteria value < 5 .

If you remove the WHERE clause, the recursive query should run endlessly. I don't understand the ORA error here, because you can never get in a cycle, as the value is always increasing. You can run out of memory or exceed the maximum allowed integer value, but not get in a cycle. So I think the error message is misleading.

If PostgreSQL uses lazy evaluation here, you are lucky. I don't think that they guarantee this to happen. Always have a WHERE clause to end recursion in your recursive cte.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM