Views
Views are saved SQL queries that behave like virtual tables. They simplify complex queries, enforce security, and act as the foundation for reporting and API layers in production systems.
Standard Views
Create, query and manage regular views
Create View
A standard view is created with CREATE VIEW. The query inside is never executed at creation time — only when someone queries the view.
Basic syntax:
SELECT * FROM v_active_users. The view hides the is_deleted and is_active complexity from the application code.
A more complex view joining multiple tables — a very common pattern:
Replace & Drop Views
Use CREATE OR REPLACE VIEW to update a view without dropping it first. Use DROP VIEW to remove it.
Why use Standard Views?
SELECT * FROM v_orders.Updatable Views
When can you INSERT / UPDATE / DELETE through a view?
What Makes a View Updatable?
PostgreSQL can automatically allow INSERT, UPDATE, and DELETE on a view — but only if the view meets strict conditions:
| Rule | Must Be True? | Why |
|---|---|---|
| Single base table (no JOINs) | Yes | PG must know which table to update |
| No GROUP BY / HAVING | Yes | Aggregated rows can't map back to single rows |
| No DISTINCT | Yes | One view row must = one table row |
| No aggregate functions (SUM, COUNT…) | Yes | Can't write back to computed values |
| No window functions | Yes | Same reason — computed, not stored |
| No UNION / INTERSECT / EXCEPT | Yes | Ambiguous which table to touch |
A simple view that qualifies:
WITH CHECK OPTION
By default, you can UPDATE a row through a view so it no longer matches the view's WHERE clause — the row disappears from the view. WITH CHECK OPTION prevents that.
SET is_active = FALSE
WHERE user_id = 42;
-- Row vanishes from view silently!
SET is_active = FALSE
WHERE user_id = 42;
-- ERROR: violates WITH CHECK OPTION
🧠 Quick check — which view is automatically updatable?
INSTEAD OF Triggers (for complex views)
When your view is too complex to be automatically updatable (e.g. it has a JOIN), you can add an INSTEAD OF trigger to handle writes yourself.
Materialized Views
Cached query results — the key to fast reporting
Standard View vs Materialized View
| Feature | Standard View | Materialized View |
|---|---|---|
| Stores data? | No (virtual) | Yes (on disk) |
| Speed | Same as base query | Very fast (pre-computed) |
| Data freshness | Always fresh | Stale until refreshed |
| Indexable? | No | Yes! |
| Updatable? | Sometimes | No |
| Best for | Always-current data, simple filtering | Dashboards, reports, expensive aggregations |
Creating a Materialized View
Use CREATE MATERIALIZED VIEW. The query runs immediately and results are stored.
Refresh
Materialized views don't auto-update. You must call REFRESH MATERIALIZED VIEW to rebuild them from the latest data.
Concurrent Refresh
REFRESH MATERIALIZED VIEW CONCURRENTLY rebuilds in the background. Readers can still query the old data while the new data is being computed.
🎮 Interactive: See how refresh strategies compare
Reporting Patterns
In production systems, materialized views are used with a scheduler (pg_cron, a cron job, or Celery) to refresh on a schedule. Here are the most common patterns:
A real scheduled refresh using pg_cron:
Real-World Example: AI SaaS App
All three view types used together in one system
Scenario — Multi-tenant AI Chat SaaS
You're building a ChatGPT-style product. You have organizations, users, conversations, and messages tables. Here's how views tie it all together.
Quick Reference Cheat Sheet
All the commands at a glance
| Type | Stores Data | Refresh Needed | Indexable | Use When |
|---|---|---|---|---|
| Standard View | No | Never | No | API layer, always-fresh data |
| Updatable View | No | Never | No | Simplify writes + hide columns |
| Materialized View | Yes | Yes (manual/cron) | Yes | Dashboards, reports, billing |