ViewsSupported
Views in zmdb are declarative schema objects that encapsulate reusable SELECT queries. They provide a way to define virtual tables based on the result of a query, which is particularly useful for complex joins, aggregations, or exposing a simplified API over normalized data.
zmdb treats views as pure DDL declarations — you define them once and let the migration system handle creation/dropping. Views are not automatically synced with schema changes; you must manually update them when underlying tables change.
Creating a Simple View#
Use createViewDdl from @zmdb/sql/schema-objects to generate the DDL for a view. The function accepts a ViewDef with the view name and SELECT query.
import { postgres } from '@zmdb/postgres';
import { createViewDdl } from '@zmdb/sql/schema-objects';
const viewDef = {
name: 'user_with_post_count',
select: `SELECT u.id, u.email, COUNT(p.id) AS post_count
FROM users u
LEFT JOIN posts p ON u.id = p.author_id
GROUP BY u.id, u.email`,
};
const ddl = createViewDdl(viewDef, postgres);
console.log(ddl);CREATE VIEW "user_with_post_count" AS SELECT u.id, u.email, COUNT(p.id) AS post_count FROM users u
LEFT JOIN posts p ON u.id = p.author_id
GROUP BY u.id, u.emailMaterialized Views#
Materialized views store the result of the query physically on disk, making them useful for expensive aggregations or frequently accessed data that doesn't need to be real-time. PostgreSQL is the only supported dialect.
import { postgres } from '@zmdb/postgres';
import { createViewDdl, UnsupportedFeatureError } from '@zmdb/sql/schema-objects';
// Only works on PostgreSQL
const materializedDef = {
name: 'sales_summary',
select: `SELECT region, SUM(amount) AS total_sales
FROM sales
GROUP BY region`,
materialized: true,
};
const ddl = createViewDdl(materializedDef, postgres);
console.log(ddl);CREATE MATERIALIZED VIEW "sales_summary" AS SELECT region, SUM(amount) AS total_sales
FROM sales
GROUP BY regionMaterialized views require periodic refreshes. Use REFRESH MATERIALIZED VIEW "view_name" to update the data. On MySQL, SingleStore, SQLite or SQL Server, this will throw UnsupportedFeatureError; Cockroach inherits the Postgres form.
Dropping Views#
When migrating, you may need to drop existing views before recreating them. Use dropViewDdl for this.
import { postgres } from '@zmdb/postgres';
import { dropViewDdl } from '@zmdb/sql/schema-objects';
const dropDdl = dropViewDdl('user_with_post_count', postgres);
console.log(dropDdl);DROP VIEW IF EXISTS "user_with_post_count"Using Views in Queries#
Once a view exists in your database, you can query it like a regular table using zmdb's query compiler. The view's columns become available through standard SELECT operations.
import { createQueryCompiler, trustedTable } from '@zmdb/sql';
import { postgres } from '@zmdb/postgres';
const compiler = createQueryCompiler(postgres);
const query = compiler.selectFrom(trustedTable('user_with_post_count')).select(['id', 'email', 'post_count']).where('post_count', '>', 5).orderBy('post_count', 'desc').limit(10).compile();
console.log(query.text);
console.log(query.parameters);SELECT "id", "email", "post_count" FROM "user_with_post_count" WHERE "post_count" > $1 ORDER BY "post_count" DESC LIMIT 10
-- parameters: [5]Views are read-only in most databases. If you need to modify data through a view, you'll need to define INSTEAD OF triggers or use an updatable view with the proper constraints.
Related#
- Indexes & Constraints — optimize view queries with indexes
- Sequences — another schema object for auto-incrementing values
- Schema Declaration — defining tables that views query