How to fix Materialized view has not been populated in PostgreSQL

PostgreSQLBEGINNERMEDIUMUpdated Dec 29, 2025· 165 views

This error occurs when you try to query a materialized view that was created with the WITH NO DATA clause but has never been populated. PostgreSQL requires the underlying query to be executed and the results stored before the view can be queried. Fix it by running REFRESH MATERIALIZED VIEW.

Quick fix

Fixes Materialized view has not been populated

REFRESH MATERIALIZED VIEW your_view_name;
  1. Populate the materialized view with REFRESH
  2. Verify the view is now populated
  3. Use REFRESH MATERIALIZED VIEW CONCURRENTLY for non-blocking refresh
  4. Create materialized views with initial data in the future
postgresql — error
REFRESH MATERIALIZED VIEW your_view_name;
Materialized view has not been populated

What this error means

A materialized view stores the physical results of a query on disk, unlike regular views which execute the query every time they're accessed. When you create a materialized view with the WITH NO DATA clause, PostgreSQL creates the view structure but does not execute the underlying query or store any data. Until the view is populated, any attempt to query it results in this error. The view remains empty and inaccessible until you explicitly refresh it with the REFRESH MATERIALIZED VIEW command.

Typical symptoms

  • Querying a materialized view returns "ERROR: materialized view "viewname" has not been populated"
  • The error occurs immediately when running SELECT on a newly created materialized view
  • Other materialized views work fine, but one specific view produces this error
  • The view was created recently with the WITH NO DATA option
  • No data has been loaded into the view since its creation

Common causes

  1. Created materialized view with WITH NO DATA clause without running REFRESH afterward
  2. Attempted to query a materialized view before its first population
  3. Forgot to execute the initial REFRESH MATERIALIZED VIEW after creating the view
  4. Rolled back a REFRESH operation, leaving the view unpopulated

How to fix "Materialized view has not been populated"

1Populate the materialized view with REFRESH

The immediate fix is to populate the materialized view by running the REFRESH command:

sql
REFRESH MATERIALIZED VIEW your_view_name;

Replace your_view_name with the actual name of your materialized view. This executes the underlying query and stores the results, making the view queryable. The operation will block other transactions from accessing the view until complete.

2Verify the view is now populated

After refreshing, query the view to confirm it now contains data:

sql
-- Check the row count
SELECT COUNT(*) FROM your_view_name;

-- Or examine some sample data
SELECT * FROM your_view_name LIMIT 10;

If this query succeeds, the view is properly populated and accessible.

3Use REFRESH MATERIALIZED VIEW CONCURRENTLY for non-blocking refresh

For views that are frequently accessed, you can avoid blocking queries during refresh by using the CONCURRENTLY option:

sql
REFRESH MATERIALIZED VIEW CONCURRENTLY your_view_name;

This allows other transactions to continue reading from the view while the refresh happens. However, this requires a unique index on the materialized view:

sql
-- First, create a unique index on the view
CREATE UNIQUE INDEX idx_view_key ON your_view_name (primary_key_column);

-- Then use CONCURRENTLY
REFRESH MATERIALIZED VIEW CONCURRENTLY your_view_name;

Without the unique index, the CONCURRENTLY option will fail.

4Create materialized views with initial data in the future

To prevent this error going forward, populate materialized views immediately upon creation by omitting the WITH NO DATA clause:

sql
-- Good: creates and populates immediately
CREATE MATERIALIZED VIEW my_view AS
SELECT id, name, COUNT(*) as count
FROM users
GROUP BY id, name;

-- Avoid: creates but does not populate
CREATE MATERIALIZED VIEW my_view AS
SELECT id, name, COUNT(*) as count
FROM users
GROUP BY id, name
WITH NO DATA;
-- requires: REFRESH MATERIALIZED VIEW my_view;

Use WITH NO DATA only when you need to define the view structure before the underlying tables are ready, or when the refresh takes too long to block creation.

5Set up automatic refresh schedules

Since materialized views are static snapshots, they become stale over time. Set up automatic refreshes using the pg_cron extension:

sql
-- Install pg_cron if not already installed
CREATE EXTENSION IF NOT EXISTS pg_cron;

-- Schedule daily refresh at 2 AM
SELECT cron.schedule('refresh_my_view', '0 2 * * *', 'REFRESH MATERIALIZED VIEW CONCURRENTLY my_view');

-- Or hourly refresh
SELECT cron.schedule('refresh_my_view', '0 * * * *', 'REFRESH MATERIALIZED VIEW my_view');

-- List scheduled jobs
SELECT * FROM cron.job;

-- Cancel a scheduled job
SELECT cron.unschedule('refresh_my_view');

Alternatively, use application-level scheduling or trigger refreshes from your application code after data modifications.

Advanced notes

Materialized views are different from regular views: regular views are just stored queries that execute every time they're accessed, while materialized views pre-compute and store results. This makes them faster for complex aggregations but requires manual refresh to stay current. The CONCURRENT REFRESH option requires a unique index because PostgreSQL must identify which rows changed to perform incremental updates instead of a full rebuild. On large materialized views, a full refresh can consume significant I/O and CPU—always test refresh times before deploying them to production. If your underlying tables change frequently, consider whether a regular view, indexed table, or materialized view with appropriate refresh frequency is the best choice. For views that must always be fresh, regular views are better; for heavy analytics queries, materialized views are ideal; for something in between, consider triggers to maintain a materialized table manually.

Frequently asked questions

What does "Materialized view has not been populated" mean?
A materialized view stores the physical results of a query on disk, unlike regular views which execute the query every time they're accessed. When you create a materialized view with the WITH NO DATA clause, PostgreSQL creates the view structure but does not execute the underlying query or store any data. Until the view is populated, any attempt to query it results in this error.
How do I fix "Materialized view has not been populated"?
This error occurs when you try to query a materialized view that was created with the WITH NO DATA clause but has never been populated. PostgreSQL requires the underlying query to be executed and the results stored before the view can be queried. Fix it by running REFRESH MATERIALIZED VIEW.
What causes "Materialized view has not been populated"?
Created materialized view with WITH NO DATA clause without running REFRESH afterward Attempted to query a materialized view before its first population Forgot to execute the initial REFRESH MATERIALIZED VIEW after creating the view
Is the "Materialized view has not been populated" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading