October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

When CTEs and Subqueries Materialize: How to Read the Query Plan

A CTE does not automatically create a temporary table. Learn how PostgreSQL 17 and MySQL choose between folding or merging and materialization—and how to check the plan.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

No: a CTE is not automatically slower than a subquery, and writing WITH does not by itself create a temporary table. A database optimizer can fold or merge either kind of query expression into its parent, or materialize an intermediate result. Which happens depends on the database, its version, the query’s shape, and sometimes optimizer controls. To understand whether a query is spooling or spilling, inspect its execution plan rather than inferring behavior from its SQL formatting.

What is the difference between a CTE and a subquery?

A subquery is a SELECT nested inside another query. It can supply a value, filter rows through a predicate such as IN or EXISTS, or act as a derived table in the FROM clause. A common table expression (CTE) is a query introduced with WITH and given a name that can be used elsewhere in the statement.

As an Amazon Associate I earn from qualifying purchases.

That distinction is about how the query is written, not necessarily how it runs. Optimizers can transform query expressions before execution. PostgreSQL describes WITH as a way to break a complicated query into simpler parts, while its PostgreSQL 17 documentation also says eligible CTEs can be folded into the parent query. MySQL 8.4 likewise documents merging and materialization as alternative strategies for derived tables, views, and CTEs. A subquery does not inherently run once per outer row, and a CTE does not inherently spool to a temporary table.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What do folding, merging, and materialization mean?

Folding or merging

When the optimizer folds or merges a query expression, it incorporates that expression into the surrounding query plan rather than first storing its complete result as a separate intermediate table. This can expose more of the combined query to optimization. For example, an outer filter may be pushed toward the underlying table scan, potentially reducing the rows processed earlier.

Materialization

Materialization means the engine produces an intermediate result and stores it for later use in the query. Readers sometimes call this “spooling.” It can be beneficial if producing the result is expensive and the query uses it more than once. It can also add work and storage, especially when the intermediate result is large. Whether that storage is in memory or on disk, and how the engine reports it, depends on the database and the particular operation.

MySQL’s 26.7 manual describes subquery materialization as using an in-memory temporary table when possible, with on-disk storage as a fallback if the table becomes too large. That is a MySQL-specific description, not a universal rule for CTEs or a claim about every MySQL version. The PostgreSQL references discussed here do not establish a corresponding memory threshold or spill statistic.

How PostgreSQL 17 handles CTEs

In PostgreSQL 17, a nonrecursive CTE can be folded into its parent if it is side-effect-free—that is, a SELECT containing no volatile functions. By default, PostgreSQL folds such a CTE when the parent references it once, but does not fold it by default when the parent references it more than once.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For eligible CTEs, PostgreSQL provides MATERIALIZED and NOT MATERIALIZED annotations to influence that choice. NOT MATERIALIZED can make it possible for outer restrictions to be applied directly to base-table scans. Materialization, on the other hand, can keep an expensive expression from being recomputed for multiple uses. Neither annotation is a blanket performance fix: the best choice depends on what the plan does with the rows and how often it must produce them.

Recursive CTEs are a separate case. PostgreSQL describes their evaluation as iterative, using working and intermediate tables as the recursion proceeds. That mechanism should not be confused with the fold-or-materialize choice for an ordinary, nonrecursive CTE.

How MySQL 8.4 handles CTEs and derived tables

MySQL 8.4 documents merging a query block into its parent or materializing it into an internal temporary table. It aims to avoid unnecessary materialization when possible, which can allow conditions to be pushed down from an outer query. Materialization may also be delayed until its result is needed; if earlier join processing makes that result unnecessary, the engine may skip producing it.

Some query features prevent merging in MySQL 8.4. The documented examples include aggregation, window functions, DISTINCT, GROUP BY, HAVING, LIMIT, and UNION or UNION ALL. MERGE and NO_MERGE hints can influence the strategy where other rules do not prevent it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If MySQL materializes a CTE, it materializes it once for the query even when multiple references use it. MySQL documents recursive CTEs as always materialized. These are MySQL behaviors; do not assume PostgreSQL or another engine makes the same choice.

How the documented behaviors compare

Engine and version Documented behavior What it means for diagnosis
PostgreSQL 17 A nonrecursive, side-effect-free CTE referenced once is normally folded; one referenced multiple times is not folded by default. Eligible CTEs can use MATERIALIZED or NOT MATERIALIZED. Check whether the CTE’s reference count and eligibility fit the default, then confirm the actual plan.
MySQL 8.4 Derived tables, views, and CTEs can be merged or materialized. A materialized CTE is produced once per query even if referenced several times; recursive CTEs are always materialized. Look for the chosen strategy and whether merge-blocking query features apply.
MySQL 26.7 For subquery materialization, an in-memory temporary table may fall back to on-disk storage if it becomes too large. Treat this as a version-scoped account of MySQL subquery behavior, not a cross-engine spill rule.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How to tell whether a query is materialized

Start with the engine and version, then read that version’s plan output. The SQL text alone cannot establish whether the optimizer folded, merged, or materialized an expression. In MySQL, the manual documents plan and trace cues, but the labels are MySQL-specific rather than portable SQL terminology.

  1. Identify the exact engine and release. The examples above are scoped to PostgreSQL 17 and MySQL 8.4 or 26.7; optimizer behavior can differ across releases.
  2. Inspect an execution plan for the query. PostgreSQL and MySQL both provide EXPLAIN. Use the output to check whether the expression appears as part of a combined plan or as a separate intermediate operation. Interpret the plan using documentation for that engine and version.
  3. For MySQL, use the relevant documented cues. Its documentation describes SUBQUERY versus DEPENDENT SUBQUERY and extended EXPLAIN text containing terms such as materialize or materialized-subquery. For CTE handling, optimizer trace output may show creating_tmp_table and reusing_tmp_table.
  4. Compare plans for controlled variants only when the engine supports the control. In PostgreSQL, compare eligible CTE forms with and without MATERIALIZED or NOT MATERIALIZED. In MySQL, relevant merge hints may be available if other rules do not bar merging. A hint changes what the optimizer may choose; it is not evidence that the resulting plan is faster.
  5. Measure on representative data. Compare actual rows with estimates, the amount of intermediate data, and execution time or temporary I/O where the engine exposes them. A plan that saves repeated computation may still lose if it processes and stores a much larger result than a pushed-down plan.

There is no portable spill label or universal diagnostic threshold established here. If a plan suggests temporary storage, consult the documentation for the exact engine and release before concluding that it spilled to disk.

When should you choose a CTE or a subquery?

Choose the form that makes the query easiest to understand, then verify the plan when performance matters. A CTE can give a complex expression a name and make its reuse clearer. A subquery can keep a small operation local to the expression that needs it. Neither form is a reliable performance shortcut across engines.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When comparing alternatives, focus on the actual work:

  • Was the expression folded or merged, or was it materialized?
  • Could the optimizer push predicates toward base-table scans, and were useful indexes still available in the chosen plan?
  • If the result is referenced more than once, does the engine reuse one materialized result or repeat work?
  • How many rows and how much data width does the intermediate result contain, and what temporary storage does the engine use?
  • Do estimated and actual row counts support the plan’s assumptions?
  • Does recursion or a feature that blocks merging dictate an engine-specific strategy?

Materialization may avoid repeated computation; folding or merging may preserve predicate pushdown and broader optimization. Those are competing benefits, not a universal ranking. No runtime benchmark or quantified speed advantage is established for CTEs versus subqueries; decide from a plan and measurement on representative data.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.