Chapter 4: “Views, Materialized Views, and CTEs”

As you start to create more complex queries they could become harder to read, harder to debug, and harder to reuse. In order to write cleaner and more maintainable queries we can break them down into logical elements. SQL provides us with 3 tools for this: Views, Materialized Views, and Common Table Expressions (CTEs). These structures allow you to reuse important queries saving you time and energy while also making your queries a lot more readable.

Views:

A view is a virtual table that is based on a SELECT query, it does not store the data itself, but it runs the underlying query each time you use it. Here is an example of creating a view and using a view.

CREATE VIEW high_gpa_students AS
SELECT name, gpa
FROM students
WHERE gpa > 3.5;
    

Using the View:

SELECT * FROM high_gpa_students

As you can see from the view that we created and how we used it. You can treat a view as a table. The most common use cases for views are to simplify frequently used queries, to expose a clean interface to users, and to compute a result derived from a table in which the table is updated frequently and you need up to date results.

Common Table Expressions (CTEs):

A CTE is a temporary result set defined by using the WITH clause; it makes your query more readable by giving a name to an intermediate result. Once the CTE is defined the output is immediately used and discarded.

CTE Example:

WITH avg_grades AS (
  SELECT student_id, AVG(grade) AS avg_grade
  FROM grades
  GROUP BY student_id
)
SELECT s.name, a.avg_grade
FROM students s
JOIN avg_grades a ON s.student_id = a.student_id
WHERE a.avg_grade > 85;
    

CTEs are great for: Breaking queries into logical steps, Avoiding repeated subqueries, and when you only need to compute a result for a one-time use.

Recursive CTE Example (Advanced)

WITH RECURSIVE countdown(n) AS (
  SELECT 5
  UNION ALL
  SELECT n - 1 FROM countdown WHERE n > 1)
SELECT * FROM countdown;
    

Materialized VIew:

Materialized Views are precomputed query results, a materialized view stores the result of a query physically in the database. This makes querying much faster, but the data becomes stale unless refreshed.

Creating a Materialized View:

CREATE MATERIALIZED VIEW student_counts_by_major AS
SELECT major_id, COUNT(*) AS student_count
FROM students
GROUP BY major_id;
    

Using the Materialized View:

SELECT * FROM student_counts_by_major;

Refreshing the Materialized View:

REFRESH MATERIALIZED VIEW student_counts_by_major;

Use Cases of a Materialized View:

  • Large aggregate queries that don’t change often
  • Dashboards or reports
  • Caching expensive computations

The caveats of using a Materialized View:

  • Requires manual or scheduled refresh
  • Consumes storage space
  • Not supported by all DBMSs (PostgreSQL has strong support)