3 ms·
That very much depends on the CTE. The release notes say this: > CTEs are automatically inlined if they have no side-effects, are not recursive, and are refere
by pgaddict 7y ago
That very much depends on the CTE. The release notes say this:
> CTEs are automatically inlined if they have no side-effects, are not recursive, and are referenced only once in the query. Inlining can be prevented by specifying MATERIALIZED, or forced for multiply-referenced CTEs by specifying NOT MATERIALIZED. Previously, CTEs were never inlined and were always evaluated before the rest of the query.
So if you have a CTE with data modifications (e.g. no DELETE/INSERT/UPDATE), then that will not be inlined.
The problem the inlining is trying to solve is that CTEs block optimizations. Consider an example like this:
CREATE TABLE t (id SERIAL PRIMARY KEY, ...);
WITH x AS (SELECT * FROM t)
SELECT * FROM x WHERE id = 1000;
Without the inlining, the WITH serves as "optimization fence" which means PostgreSQL won't be able to use the index on the ID column. The database essentially evaluates the CTE, stashes the results somewhere (in a work_mem buffer / temp file) and then queries this intermediate result.
There are historical reasons why it was originally implemented like this, but the trouble is, people often don't realize this and use it just as a nice way to name subqueries. So they treat
WITH x AS (SELECT * FROM t)
SELECT * FROM x WHERE id = 1000;
as semantically the same as
SELECT * FROM (SELECT * FROM t) foo WHERE id = 1000;
but it was executed quite differently.
So for people using CTEs like this, the automatic inlining by default is likely a huge improvement. There may be cases of regression, but that's why it's possible to enforce materialization by using MATERIALIZED.