VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
SELECT can do.
Open the client with mysql -u root -p sakila before you start.
How a recursive CTE works
CTE_NAME with a name for the result, COLUMN_NAME with the column it
produces, SEED_VALUE with the starting value, NEXT_EXPRESSION with what
makes the next row from the previous one, and STOP_CONDITION with the test
that eventually fails.
A recursive CTE has 2 kinds of part, joined by UNION ALL:
- A seed, also called the anchor member, which mentions the CTE nowhere and produces the starting rows.
- A recursive member, which selects from the CTE itself.
UNION in place of UNION ALL, which deduplicates
each round. Neither is common and both are worth recognizing when you meet
them.
The server runs the seed, then runs the recursive part against whatever the
previous round produced, over and over, until a round produces no rows. The
WHERE in the recursive part is what eventually makes that happen.
The keyword is WITH RECURSIVE, and it goes on the WITH, not on the CTE.
Counting to 5
WHERE n < 5 produces no
next row.
A series like this is more useful than it looks. Joined against real data it
gives you a row for every day in a range, including the days nothing happened,
which no query over the data alone can produce.
Leaving out the stop condition
Drop theWHERE and the recursive part always produces another row.
ERROR 3636 as a missing or wrong stop condition first. Raising the limit with
SET SESSION cte_max_recursion_depth = 5000; is right only when you genuinely
need more rounds than the default allows. It is wrong when the query would
never have stopped, because it then takes longer to fail and fails anyway.
The setting is a variable, so the 1,000 above is this server’s value rather
than a fixed property of MySQL.
Summary
- Write
WITH RECURSIVEwhen a CTE reads itself. - Give it a seed part and a recursive member, joined by
UNION ALL. - Put the stop condition in the recursive part’s
WHERE. - Read
ERROR 3636as a runaway recursion before reaching for the setting.
See also
- Common table expressions — the non-recursive kind
- UNION — the operator joining the 2 parts
- When a MySQL CTE beats a derived table — how the optimizer handles them

