WITH before the main SELECT. Each CTE behaves like a temporary, query-scoped data source that you can reference by name from FROM, JOIN, and UNION branches.
CTEs are useful when the same intermediate result is used in more than one place, when a query reads better as a sequence of named steps, or when you want to keep a complex pre-filter out of the main SELECT.
Added in 3.2.0.
Syntax
- A single
WITHkeyword declares any number of comma-separated CTEs. - Each CTE name must be unique within the
WITHblock. - Later CTEs can reference earlier ones (forward-only chaining).
- The main
SELECTand everyJOIN/UNIONbranch can reference any declared CTE by name in theirFROMor join source. EXPLAINandEXPLAIN ANALYZEaccept a leadingWITHblock:EXPLAIN WITH ... SELECT ....
Forward chaining
A CTE body may reference any CTE declared earlier in the sameWITH block. Mutual recursion and back-references are rejected at parse time.
Using CTEs in JOIN and UNION
A CTE can appear anywhere a file source can — in the mainFROM, in any JOIN, and in any UNION branch.
Fluent API
Register named subqueries on the parent query withwith($name, $query). The CTE is then visible to any JOIN or UNION branch the parent composes. Inspect the registered CTEs with hasCte(), getCte(), and getCtes().
FROM-position CTE references are parser-only — the source stream of an already-constructed
Query is immutable, so the fluent API uses CTEs through JOIN and UNION rather than as a FROM target. Use the FQL string form when you need a CTE in FROM.Evaluation strategy
FiQueLa picks per-reference between two strategies, so a CTE used exactly once in aJOIN never pays an in-memory buffer:
Cycles (a CTE that transitively references itself) are rejected at build time with a clear error.
Errors
The parser surfaces problems before any file is opened:- Duplicate CTE name in the same
WITHblock —ParseException. WITH RECURSIVEis not supported —ParseException(theRECURSIVEkeyword exists solely to raise a targeted error).- Reference to an unknown CTE name in
FROMorJOIN—ParseExceptionlisting every declared name in scope, so typos surface immediately. DESCRIBE WITH ...— rejected (DESCRIBEexpects a source, not aSELECT).
Limitations
WITH RECURSIVEis not supported.- Nested
WITHblocks inside subqueries are not supported — declare every CTE at the top level. - CTE references inside
IN (...)andEXISTS (...)are not supported (subqueries in condition operands are not yet parsed). DESCRIBEcannot be combined withWITH.