Что такое materialized view в PostgreSQL?

MiddlePostgreSQL · Backend·Обновлено 4 июля 2026
Коротко
Materialized view (материализованное представление) — это объект базы данных, который хранит результат запроса физически на диске, в отличие от обычного представления, которое выполняет запрос каждый раз при обращении. Данные обновляются вручную командой REFRESH MATERIALIZED VIEW.

Materialized View в PostgreSQL

Материализованное представление (materialized view) — это объект БД, сочетающий в себе свойства таблицы и обычного представления. Результат SELECT-запроса сохраняется на диске как физическая копия данных, что позволяет обращаться к ней без повторного выполнения тяжёлого запроса.

Отличие от обычного VIEW

Обычный VIEW — это именованный запрос. При каждом обращении PostgreSQL выполняет лежащий в основе SELECT заново. Materialized view выполняет запрос один раз при создании или явном обновлении и сохраняет строки физически.

Характеристика VIEW MATERIALIZED VIEW
Хранение данных Нет Да (на диске)
Актуальность Всегда свежие До следующего REFRESH
Производительность чтения Зависит от запроса Высокая
Можно создать индекс Нет Да

Создание

-- Создаём материализованное представление с агрегацией продаж
CREATE MATERIALIZED VIEW sales_summary AS
SELECT
    product_id,
    DATE_TRUNC('month', created_at) AS month,
    SUM(amount)                      AS total_amount,
    COUNT(*)                         AS orders_count
FROM orders
GROUP BY product_id, DATE_TRUNC('month', created_at)
WITH DATA; -- выполнить запрос сразу и сохранить данные

Обновление данных

Данные не обновляются автоматически — нужно явно вызвать REFRESH:

-- Блокирует чтение на время обновления
REFRESH MATERIALIZED VIEW sales_summary;

-- Не блокирует чтение (требует уникального индекса)
REFRESH MATERIALIZED VIEW CONCURRENTLY sales_summary;

CONCURRENTLY позволяет читать старые данные пока идёт обновление, но работает только при наличии хотя бы одного уникального индекса на представлении.

Индексы

В отличие от обычных представлений, на materialized view можно создавать индексы:

-- Создаём индекс для быстрого поиска по товару и месяцу
CREATE UNIQUE INDEX ON sales_summary (product_id, month);

Типичные сценарии применения

  • Предагрегация аналитических данных (дашборды, отчёты)
  • Кэширование результатов сложных JOIN-запросов
  • Денормализация данных для ускорения читающих запросов
  • ETL-пайплайны внутри PostgreSQL

Стратегии обновления

AUTOMATIC обновление PostgreSQL не поддерживает, поэтому используют:

  • pg_cron — расписание прямо в БД
  • Cron на уровне ОС — вызов REFRESH по расписанию
  • Триггеры на исходных таблицах — обновление при изменении данных
  • Логику приложения — вызов REFRESH после массовых загрузок

Удаление

DROP MATERIALIZED VIEW sales_summary;

Что хочет услышать интервьюер

Кандидат чётко объясняет ключевое отличие от обычного VIEW: данные хранятся физически, а не вычисляются на лету

Знает команду REFRESH MATERIALIZED VIEW и понимает, что данные могут устаревать между обновлениями

Понимает разницу между REFRESH и REFRESH CONCURRENTLY, в том числе требование уникального индекса для CONCURRENTLY

Называет практические сценарии: аналитика, дашборды, агрегации, кэширование тяжёлых запросов

Осознаёт компромисс: выигрыш в скорости чтения vs возможная неактуальность данных и затраты на REFRESH

Пример: Создание, индексация и обновление materialized view

-- Создание материализованного представления
CREATE MATERIALIZED VIEW monthly_revenue AS
SELECT
    DATE_TRUNC('month', paid_at) AS month,
    currency,
    SUM(amount)                  AS revenue,
    COUNT(DISTINCT user_id)      AS paying_users
FROM payments
WHERE status = 'completed'
GROUP BY DATE_TRUNC('month', paid_at), currency
WITH DATA;

-- Индекс для ускорения фильтрации и для CONCURRENTLY
CREATE UNIQUE INDEX ON monthly_revenue (month, currency);

-- Обновление без блокировки чтения
REFRESH MATERIALIZED VIEW CONCURRENTLY monthly_revenue;

-- Чтение работает как обычная таблица
SELECT * FROM monthly_revenue
WHERE month >= '2026-01-01'
ORDER BY month DESC;

Пример: Сравнение VIEW и MATERIALIZED VIEW

-- Обычный VIEW: тяжёлый запрос выполняется каждый раз
CREATE VIEW slow_report AS
SELECT u.id, u.name, COUNT(o.id) AS orders, SUM(o.total) AS spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name;

-- Materialized VIEW: запрос выполняется один раз при REFRESH
CREATE MATERIALIZED VIEW fast_report AS
SELECT u.id, u.name, COUNT(o.id) AS orders, SUM(o.total) AS spent
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
GROUP BY u.id, u.name
WITH DATA;

-- Оба вызываются одинаково, но производительность разная
SELECT * FROM fast_report WHERE spent > 10000;

Типичные ошибки

Путают materialized view с обычным VIEW — считают, что данные тоже вычисляются при каждом запросе

Не знают об ограничении CONCURRENTLY: думают, что его можно использовать без уникального индекса

Предполагают, что данные обновляются автоматически при изменении исходных таблиц

Не упоминают возможность создания индексов на materialized view, что является одним из главных преимуществ

Не понимают trade-off: materialized view подходит для данных, допускающих небольшое устаревание, и не подходит там, где нужна актуальность в реальном времени

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее