Skip to content

Expert: design, speed and engines · Indexes, query plans and views · topic 51 of 62 · 30 min

Views and materialized views

Explanation

A view is a saved query with a name that you can SELECT from like a table. Views simplify reports and can hide private columns, but they store no data and are not faster than their query. A materialized view (PostgreSQL) or indexed view (SQL Server) stores the result and must be refreshed; in SQLite you emulate one with a summary table rebuilt by DELETE plus INSERT ... SELECT.

A view is a saved query

Some queries are written again and again: "delivered orders with their totals" appears in the sales report, the finance export and the dashboard. A view gives that query a name. After CREATE VIEW, you can SELECT from it, join it and filter it as if it were a table.

sql
CREATE VIEW delivered_order_totals AS
SELECT o.id AS order_id, c.name AS customer, o.order_date,
       SUM(oi.qty * oi.price) AS total
FROM orders o
JOIN customers c    ON c.id = o.customer_id
JOIN order_items oi ON oi.order_id = o.id
WHERE o.status = 'Delivered'
GROUP BY o.id, c.name, o.order_date;

SELECT customer, COUNT(*) AS orders, SUM(total) AS spent
FROM delivered_order_totals
GROUP BY customer
ORDER BY spent DESC;
-- Ravi Kumar | 2 | 6497
-- Asha Verma | 2 | 4028
The joins and the status rule are written once, in the view. Every report reuses them.
Think of it like this: A view is like a saved filter in your email app called "Bills". It does not copy any emails; it just remembers the search. Every time you open it, it runs the search again, so it always shows the latest mail.

Because a view stores only the query, its results are always current, and it uses no extra space. It is also no faster than the query inside it: the database runs that query (usually merged into yours) every time. To speed up a view, index its underlying tables.

Views for security and stable interfaces

A view can expose some columns and hide others. A support team may see customers' names and cities but not emails or phone numbers. In PostgreSQL, SQL Server and MySQL you GRANT SELECT on the view and give no access to the table itself. Views also act as a stable interface: if you later split a table, a view with the old name and columns keeps old reports working.

sql
CREATE VIEW customers_public AS
SELECT id, name, city FROM customers;
SELECT email FROM customers_public;
-- Error: no such column: email
The view simply has no email column to show.

Can you change data through a view?

Some engines let you INSERT, UPDATE or DELETE through simple views. In PostgreSQL a view is automatically updatable when it reads from one table with no GROUP BY, DISTINCT, aggregates, UNION or LIMIT; MySQL has similar rules. WITH CHECK OPTION stops changes that would make a row vanish from the view. Views with joins and totals, like delivered_order_totals, are read-only.

In SQLite every view is read-only. UPDATE customers_public SET city = 'Nagpur' fails with "cannot modify customers_public because it is a view". An INSTEAD OF trigger can redirect such changes to the real table; module 11 covers triggers.

Materialized views: storing the result

Sometimes the query behind a view is heavy, such as a year of sales grouped by city and category, and nobody needs it to the second. A materialized view runs the query once and stores the rows. Reading it is as fast as reading a small table, and you can index it. The price: it goes stale until refreshed.

sql
-- PostgreSQL
CREATE MATERIALIZED VIEW category_sales AS
SELECT p.category, SUM(oi.qty * oi.price) AS revenue
FROM order_items oi JOIN products p ON p.id = oi.product_id
GROUP BY p.category;

REFRESH MATERIALIZED VIEW category_sales;               -- rebuilds; readers wait
REFRESH MATERIALIZED VIEW CONCURRENTLY category_sales;  -- readers keep reading; needs a UNIQUE index on the view
PostgreSQL only. A scheduled job (for example every 15 minutes) usually runs the refresh.
  • SQL Server: an indexed view. Create the view WITH SCHEMABINDING, then a UNIQUE CLUSTERED INDEX on it; SQL Server then keeps the stored result up to date on every change.
  • Oracle: materialized views with several refresh options.
  • MySQL and SQLite: no materialized views. Use a summary table.

Emulating one in SQLite: a summary table

sql
CREATE TABLE category_sales (category TEXT PRIMARY KEY, revenue INTEGER NOT NULL);

-- Refresh (run on a schedule, inside one transaction):
BEGIN;
DELETE FROM category_sales;
INSERT INTO category_sales (category, revenue)
SELECT p.category, SUM(oi.qty * oi.price)
FROM order_items oi
JOIN orders o   ON o.id = oi.order_id
JOIN products p ON p.id = oi.product_id
WHERE o.status <> 'Cancelled'
GROUP BY p.category;
COMMIT;
The transaction makes the swap all-or-nothing, so readers never see an empty table.

Common mistakes

  • Expecting a plain view to be faster than its query. Fix: index the base tables, or use a materialized view or summary table.
  • Stacking views on views on views until nobody can read the plan. Fix: keep views one or two levels deep.
  • Forgetting to refresh a materialized view or summary table. Fix: schedule the refresh and show "updated at" on the report.
  • Using SELECT * inside a view. Fix: list columns, so the view does not change shape when the table does.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Views and materialized views · 1/7

Views

A view is a saved query with a name. You select from it like a table.

Prefer a video by a teacher? Find free videos on “sql views vs materialized views explained postgresql refresh materialized view” on YouTube ↗

In real life: A hospital reporting view

A hospital's billing tables hold patient names, phone numbers and diagnoses. The finance team needs daily revenue per department, but has no reason to see medical details. The database team creates a view that joins bills to departments and exposes only the date, department and amount, and grants the finance role SELECT on that view alone. A heavy monthly summary is kept in a materialized view refreshed every night.

sql
CREATE VIEW dept_revenue AS
SELECT b.bill_date, d.name AS department, SUM(b.amount_paise) AS revenue_paise
FROM bills b
JOIN departments d ON d.id = b.department_id
GROUP BY b.bill_date, d.name;
-- PostgreSQL: GRANT SELECT ON dept_revenue TO finance;
The view is the only thing the finance role can read.

Practice (0/4 done)

Task 1 · Code · SQL

Create a view named delivered_order_totals with the columns order_id, customer (the customer's name), order_date and total (the sum of qty * price), for orders with status 'Delivered'. Then query the view: return order_id, customer and total, from the largest total to the smallest.

Task 2 · Code · SQL

The support team must not see emails. Create a view customers_public with only id, name and city from customers. Then, using the view, return id, name and city of customers in 'Pune'.

Task 3 · Code · SQL

Emulate a materialized view. 1) Create table category_sales (category TEXT PRIMARY KEY, revenue INTEGER NOT NULL) and fill it with INSERT ... SELECT: revenue (sum of qty * price) per product category, excluding Cancelled orders. 2) A new order arrives: INSERT INTO orders VALUES (109, 6, '2025-04-05', 'Placed'); and INSERT INTO order_items VALUES (109, 8, 2, 1299);. 3) Refresh: DELETE everything from category_sales and run the same INSERT ... SELECT again. 4) Return category and revenue, sorted by category.

Task 4 · Question

Which PostgreSQL view can you UPDATE directly without triggers?

Quick quiz

Question 1

1.What does a plain view store?

Question 2

2.Why is a view useful for privacy?

Question 3

3.What is the main trade-off of a materialized view?

Question 4

4.SQLite has no materialized views. What is the usual substitute?

Question 5

5.In PostgreSQL, what does REFRESH MATERIALIZED VIEW CONCURRENTLY need?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What does a plain view store?

Answer: Only the query; it runs each time you use it

A view is a saved query, so it is always current and uses no space for rows.

Why is a view useful for privacy?

Answer: You can grant access to a view that leaves out private columns

Users see only the columns the view exposes.

What is the main trade-off of a materialized view?

Answer: It is fast to read but stale until refreshed

The stored result does not change until REFRESH runs.

SQLite has no materialized views. What is the usual substitute?

Answer: A summary table rebuilt with DELETE and INSERT ... SELECT on a schedule

A summary table holds the stored result; the refresh is up to you.

In PostgreSQL, what does REFRESH MATERIALIZED VIEW CONCURRENTLY need?

Answer: A UNIQUE index on the materialized view

CONCURRENTLY compares old and new rows, which needs a unique index; readers are not blocked.

In SQLite, you write INSERT INTO tickets (customer_id, subject, status) VALUES (3, 'Hi', DEFAULT). What happens?Related

Answer: A syntax error: SQLite does not accept DEFAULT in VALUES

Leave the column out instead. PostgreSQL, MySQL and SQL Server do accept DEFAULT there.

From INSERT

You want a named query that many reports and users can reuse across sessions. What fits best?Related

Answer: A view

A view is saved in the database (module 10); a CTE lives for one statement.

From Common table expressions (WITH)

The sales view has one row per order line: 12 rows for 8 orders. How do you count orders?Related

Answer: COUNT(DISTINCT order_id)

Several lines share one order_id; DISTINCT counts each order once.

From Writing reports with aggregation

Which column gets an index automatically in SQLite and PostgreSQL?Related

Answer: A column with a UNIQUE constraint

UNIQUE and PRIMARY KEY are enforced with an index. Foreign keys are not indexed automatically.

From How indexes work

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Indexes, query plans and views”. The module exam comes next.
  2. Module 10 exam: Indexes, query plans and views · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Procedures, triggers, SQL engines and NoSQL. 62 topics left in the course before the final exam.

← Previous: Writing fast queries