Features / Select
Control PostgreSQL CTE materialization
Tell PostgreSQL to materialize a CTE once or to keep it eligible for inlining when the planner would otherwise choose for you.
A common table expression (CTE) is a named query introduced by WITH and
used by the statement that follows.
Enable it
This feature is PostgreSQL-only:
import { eq } from 'drizzle-orm'
import 'drizzle-plus/pg/$withMaterialized'
It adds $withMaterialized() and $withNotMaterialized() next to Drizzle's
$with() method.
Materialize a CTE
Use $withMaterialized() when the CTE should be evaluated as its own result:
const activeUsers = db
.$withMaterialized('active_users')
.as(db.select({ id: user.id }).from(user).where(eq(user.active, true)))
const rows = await db.with(activeUsers).select().from(activeUsers)
Prefer inlining
Use $withNotMaterialized() when the CTE should remain eligible for folding
into the parent query:
const activeUsers = db
.$withNotMaterialized('active_users')
.as(db.select({ id: user.id }).from(user).where(eq(user.active, true)))
Both methods use the same builder shape as $with(). They only change the
keyword emitted after the CTE's AS clause.
Warning
PostgreSQL restricts when NOT MATERIALIZED is valid, including restrictions on volatile functions. Check the PostgreSQL CTE rules before applying either override to a complex query.