Views & Materialized Views
IntermediateA view is a named query stored in the catalog; a materialized view caches the result set on disk and must be refreshed to reflect underlying data changes.
Overview
A regular view is simply a stored SELECT statement — every time you query it, the DB rewrites your query inline with the view's definition and executes it against live data. Views simplify complex queries, provide a security layer (expose only certain columns/rows), and act as a stable API over a changing schema. A materialized view physically stores the query result and gives query-plan freedom and index support on the cached data. The trade-off is staleness: you must REFRESH MATERIALIZED VIEW manually or on a schedule. PostgreSQL supports CONCURRENTLY refresh (non-blocking) if the view has a unique index.
Regular Views
Views are non-updatable by default when they contain JOINs, aggregates, DISTINCT, or subqueries. Simple single-table views with no aggregation are often auto-updatable in PostgreSQL.
-- Create a view for the sales dashboard (hides internal columns)
CREATE OR REPLACE VIEW v_order_summary AS
SELECT
o.id AS order_id,
u.email AS customer_email,
o.total_amount,
o.status,
o.created_at
FROM orders o
JOIN users u ON u.id = o.user_id
WHERE o.status != 'cancelled';
-- Query the view exactly like a table
SELECT * FROM v_order_summary WHERE status = 'pending';
-- Security: grant access to view but not underlying tables
GRANT SELECT ON v_order_summary TO reporting_role;
-- Updatable view (simple, single-table, no aggregation)
CREATE VIEW v_active_users AS
SELECT id, email, username FROM users WHERE status = 'active';
-- This INSERT goes through to the base table:
INSERT INTO v_active_users(email, username) VALUES ('bob@x.com', 'bob');Materialized Views
Use materialized views for expensive aggregations that are read far more often than they change. Add indexes on materialized view columns exactly as you would on a table.
-- Materialized view: pre-aggregate daily revenue (expensive query)
CREATE MATERIALIZED VIEW mv_daily_revenue AS
SELECT
DATE_TRUNC('day', created_at)::DATE AS revenue_date,
COUNT(*) AS order_count,
SUM(total_amount) AS total_revenue
FROM orders
WHERE status = 'completed'
GROUP BY 1
ORDER BY 1;
-- Add an index on the materialized view for fast date range queries
CREATE UNIQUE INDEX idx_mv_daily_revenue_date ON mv_daily_revenue(revenue_date);
-- Refresh blocking (no concurrent reads during refresh)
REFRESH MATERIALIZED VIEW mv_daily_revenue;
-- Refresh non-blocking — requires a UNIQUE index on the view
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_revenue;
-- Schedule via pg_cron or an external scheduler:
-- SELECT cron.schedule('0 1 * * *', 'REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_revenue');
-- MySQL equivalent: no native materialized views — use a scheduled event + real table
CREATE TABLE mv_daily_revenue (...);
CREATE EVENT refresh_mv ON SCHEDULE EVERY 1 HOUR DO
CALL refresh_daily_revenue_proc();Key Points to Remember
- 1Regular views are rewritten inline at query time — no storage, always fresh.
- 2Materialized views store data on disk — must be refreshed to reflect changes.
- 3REFRESH MATERIALIZED VIEW CONCURRENTLY requires a UNIQUE index but does not block reads.
- 4Views are used for security (column/row-level access control) and query simplification.
- 5You can index a materialized view just like a regular table.
- 6MySQL has no native materialized views — emulate with tables + scheduled events or triggers.
Interview Questions
Sign in to ask AriaWhat is the difference between a view and a materialized view?
When would you choose a materialized view over a view?
How do you refresh a materialized view without blocking reads in PostgreSQL?
Can you update data through a view? Under what conditions?
How would you emulate materialized views in MySQL?
Ask Aria about Views & Materialized Views
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.