PostgreSQL: Views e Views Materializadas no PostgreSQL
Última atualização: 2026-08-26
1. O Que Você Vai Aprender
- CREATE VIEW / ALTER VIEW / DROP VIEW
- Views atualizáveis (views simples suportam INSERT/UPDATE/DELETE)
- WITH CHECK OPTION para prevenir que linhas escapem da view
- Views materializadas (MATERIALIZED VIEW)—uma especialidade do PostgreSQL
- REFRESH MATERIALIZED VIEW / CONCURRENTLY
- Escolhendo entre views, views materializadas e tabelas temporárias
2. A História
Alice é uma engenheira de dados em uma plataforma de e-commerce. A equipe de operações revisa um relatório resumido de vendas todos os dias às 8:00, que une 5 tabelas, agrega 3 milhões de registros de pedidos e leva 2 horas para consultar.
O chefe dela disse: "O relatório pode voltar instantaneamente?"
Alice definiu a consulta como uma view materializada, atualizada automaticamente todos os dias às 4:00 da manhã. A consulta do relatório pré-calculado caiu de 2 horas para 3 segundos. Mas os dados da view materializada não são em tempo real—esse é o compromisso entre views comuns e views materializadas.
3. Conceito: Views Comuns
(1) Sintaxe do CREATE VIEW
CREATE [OR REPLACE] VIEW nome_view [(aliases_coluna)] AS
SELECT ...;
Uma view é uma definição de consulta armazenada—não armazena dados. Cada vez que você consulta uma view, o PostgreSQL expande (reescreve) a definição da view na consulta subjacente e a executa.
| Propriedade | Descrição |
|---|---|
| O que é armazenado | Apenas o texto da consulta |
| Atualidade dos dados | Tempo real, sempre reflete os dados mais recentes da tabela base |
| Desempenho | Igual a executar a consulta subjacente diretamente |
| Espaço usado | Quase nenhum |
▶ Exemplo: Criar uma View de Resumo de Vendas
CREATE VIEW v_daily_sales AS
SELECT
order_date,
region,
COUNT(*) AS order_count,
SUM(amount) AS total_revenue,
AVG(amount) AS avg_order_value
FROM orders
GROUP BY order_date, region;
Output:
count
-------
5
(1 row)
▶ Exemplo: Consultar a View
SELECT * FROM v_daily_sales
WHERE order_date >= '2025-01-01'
ORDER BY total_revenue DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
Equivalente a escrever o SELECT subjacente diretamente—o PostgreSQL o expande automaticamente.
(2) ALTER VIEW e DROP VIEW
| Operação | Sintaxe | Descrição |
|---|---|---|
| Renomear | ALTER VIEW v RENAME TO v_novo |
Renomear a view |
| Definir coluna padrão | ALTER VIEW v ALTER COLUMN c SET DEFAULT d |
Alterar um valor padrão de coluna |
| Definir proprietário | ALTER VIEW v OWNER TO role |
Alterar o proprietário |
| Excluir | DROP VIEW [IF EXISTS] v [CASCADE] |
CASCADE exclui views dependentes |
▶ Exemplo: Modificar e Excluir uma View
ALTER VIEW v_daily_sales RENAME TO v_daily_summary;
DROP VIEW IF EXISTS v_daily_summary CASCADE;
Output:
-- instrução SQL executada com sucesso
(3) Mecanismo de Expansão da View
flowchart LR
A["SELECT * FROM v_daily_sales"] --> B["Reescrever<br/>expandir definição da view"]
B --> C["SELECT order_date, region, COUNT(*)...<br/>FROM orders<br/>GROUP BY ..."]
C --> D[Otimizador]
D --> E[Executar]
style B fill:#fff9c4
style D fill:#c8e6c9
4. Conceito: Views Atualizáveis
(1) Quais Views São Atualizáveis
No PostgreSQL, views simples que atendem a todas as seguintes condições são automaticamente atualizáveis (suportam INSERT / UPDATE / DELETE):
| Condição | Descrição |
|---|---|
| FROM de uma única tabela base | Sem JOIN |
| Sem GROUP BY / HAVING | Sem agregação |
| Sem DISTINCT | Sem desduplicação |
| Sem funções de janela | Sem OVER |
| Sem operações de conjunto | Sem UNION / INTERSECT / EXCEPT |
| Colunas SELECT são colunas da tabela base | Sem expressões/colunas calculadas |
▶ Exemplo: View Atualizável
CREATE VIEW v_active_users AS
SELECT user_id, name, email, status
FROM users
WHERE status = 'active';
Output:
CREATE TABLE
UPDATE v_active_users SET name = 'Alice Wang' WHERE user_id = 1;
DELETE FROM v_active_users WHERE user_id = 99;
INSERT INTO v_active_users (user_id, name, email, status)
VALUES (101, 'Charlie', 'charlie@example.com', 'active');
(2) WITH CHECK OPTION
Por padrão, linhas atualizadas/inseridas através de uma view podem "escapar" do escopo da view (ex.: alterar status para 'inactive', e essa linha não aparece mais na view). WITH CHECK OPTION proíbe tal escape.
| Opção | Comportamento |
|---|---|
| Sem CHECK OPTION | Escape permitido; linhas atualizadas podem não aparecer mais na view |
| WITH CHECK OPTION | Escape proibido; linhas atualizadas devem ainda satisfazer a condição da view |
| WITH CASCADED CHECK OPTION | View atual + views dependentes verificadas (recursivo) |
| WITH LOCAL CHECK OPTION | Apenas a condição da view atual verificada |
▶ Exemplo: WITH CHECK OPTION Previne Escape
CREATE VIEW v_active_users_strict AS
SELECT user_id, name, email, status
FROM users
WHERE status = 'active'
WITH CHECK OPTION;
UPDATE v_active_users_strict SET status = 'inactive' WHERE user_id = 1;
ERRO: nova linha viola a check option da view "v_active_users_strict"
DETALHE: Linha com falha contém (1, ..., inactive).
▶ Exemplo: Inserir em View Atualizável e Depois Consultar
INSERT INTO v_active_users (user_id, name, email, status)
VALUES (200, 'Bob', 'bob@example.com', 'active');
SELECT * FROM v_active_users WHERE user_id = 200;
user_id | name | email | status
---------+------+--------------------+--------
200 | Bob | bob@example.com | active
5. Conceito: Views Materializadas
(1) Visão Geral de MATERIALIZED VIEW
Uma view materializada realmente armazena o resultado da consulta em disco; as consultas leem os dados pré-calculados diretamente, sem reexecutar a consulta subjacente. Esta é uma especialidade do PostgreSQL.
| Propriedade | View comum | View materializada |
|---|---|---|
| O que é armazenado | Texto da consulta | Texto da consulta + dados do resultado |
| Atualidade dos dados | Tempo real | Atualiza apenas na atualização |
| Desempenho da consulta | Igual à consulta subjacente | Extremamente rápido (lê pré-calculado) |
| Espaço usado | Quase nenhum | Mesmo tamanho do conjunto de resultados |
| Atualizável | Views simples podem ser | Sem DML direto |
| Método de atualização | Não necessário | REFRESH MATERIALIZED VIEW |
▶ Exemplo: Criar uma View Materializada
CREATE MATERIALIZED VIEW mv_monthly_sales AS
SELECT
DATE_TRUNC('month', order_date)::date AS month,
region,
COUNT(*) AS order_count,
SUM(amount) AS total_revenue,
AVG(amount) AS avg_order_value
FROM orders
GROUP BY DATE_TRUNC('month', order_date), region
WITH DATA;
Output:
count
-------
5
(1 row)
▶ Exemplo: Consultar a View Materializada
SELECT * FROM mv_monthly_sales
WHERE month >= '2025-01-01'
ORDER BY total_revenue DESC;
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
Retorna em milissegundos, porque os dados já estão pré-calculados e armazenados.
(2) REFRESH
| Sintaxe | Comportamento | Bloqueio | Velocidade |
|---|---|---|---|
REFRESH MATERIALIZED VIEW mv |
Atualização completa, substitui todos os dados | Adquire bloqueio exclusivo, bloqueia leituras | Mais lento |
REFRESH MATERIALIZED VIEW CONCURRENTLY mv |
Atualização incremental (precisa de índice único) | Não bloqueia leituras | Mais rápido |
CONCURRENTLY é uma especialidade do PostgreSQL: a view permanece consultável durante a atualização—o negócio não é bloqueado.
▶ Exemplo: Atualização Completa
REFRESH MATERIALIZED VIEW mv_monthly_sales;
Output:
-- instrução SQL executada com sucesso
Todos os SELECTs nesta view materializada são bloqueados durante a atualização.
▶ Exemplo: Atualização Incremental CONCURRENTLY
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_monthly_sales;
Output:
-- instrução SQL executada com sucesso
Pré-requisito: a view materializada deve ter pelo menos um índice único.
CREATE UNIQUE INDEX idx_mv_monthly_sales_pk
ON mv_monthly_sales (month, region);
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_monthly_sales;
(3) WITH DATA vs WITH NO DATA
| Opção | Comportamento |
|---|---|
| WITH DATA | Preencher dados imediatamente na criação (padrão) |
| WITH NO DATA | Não preencher na criação; deve fazer REFRESH antes da primeira consulta |
▶ Exemplo: Preenchimento Diferido
CREATE MATERIALIZED VIEW mv_expensive_report AS
SELECT ... FROM ... WITH NO DATA;
REFRESH MATERIALIZED VIEW mv_expensive_report;
Output:
CREATE TABLE
▶ Exemplo: Atualização Automática Programada (extensão pg_cron)
SELECT cron.schedule(
'refresh_monthly_sales',
'0 4 * * *',
$$REFRESH MATERIALIZED VIEW CONCURRENTLY mv_monthly_sales$$
);
Output:
id | name | value
----+----------+-------
1 | example | 42
(1 row)
Atualiza automaticamente todos os dias às 4:00 da manhã.
6. Views vs Views Materializadas vs Tabelas Temporárias
| Dimensão | View comum | View materializada | Tabela temporária |
|---|---|---|---|
| Armazena definição da consulta | Sim | Sim | Não |
| Armazena dados | Não | Sim | Sim |
| Atualidade dos dados | Tempo real | Atualização manual | Manutenção manual |
| Desempenho da consulta | Depende da consulta subjacente | Extremamente rápido | Rápido |
| Persistência entre sessões | Sim | Sim | Não (desaparece após a sessão) |
| Suporta índices | Não (usa índices da tabela base) | Sim | Sim |
| Suporta DML | Views simples atualizáveis | Não | Sim |
| Cenário típico | Simplificar consultas | Pré-computação de relatórios | Resultados intermediários temporários |
classDiagram
class View {
+armazena texto da consulta
+dados em tempo real
+view simples permite DML
+WITH CHECK OPTION
}
class MaterializedView {
+armazena texto da consulta+dados
+REFRESH para atualizar
+CONCURRENTLY
+indexável
+WITH DATA/NO DATA
}
class TempTable {
+armazena apenas dados
+desaparece após a sessão
+totalmente compatível com DML
+indexável
}
View <|-- MaterializedView : estende
MaterializedView ..|> TempTable : desempenho similar
7. Melhores Práticas de Gerenciamento de Views
(1) Convenções de Nomenclatura
| Tipo | Prefixo recomendado | Exemplo |
|---|---|---|
| View comum | v_ |
v_active_users |
| View materializada | mv_ |
mv_monthly_sales |
| Tabela temporária | tmp_ |
tmp_import_data |
(2) Dependências de Views e Segurança
| Operação | Risco | Solução |
|---|---|---|
| Excluir tabela base | View se torna inválida | DROP TABLE CASCADE exclui automaticamente views dependentes |
| Alterar coluna da tabela base | View pode gerar erro | Atualizar definição com CREATE OR REPLACE VIEW |
| Controle de privilégio | View pode limitar visibilidade de colunas | GRANT SELECT ON view TO role |
▶ Exemplo: Privilégios em Nível de Coluna com uma View
CREATE VIEW v_user_public AS
SELECT user_id, name
FROM users;
GRANT SELECT ON v_user_public TO reporter_role;
REVOKE SELECT ON users FROM reporter_role;
Output:
CREATE TABLE
reporter_role só pode ver user_id e name, não colunas sensíveis como email.
8. Exemplo Completo
Otimização de relatório de Alice—pré-computação com view materializada + atualização CONCURRENTLY + view para simplificar consultas:
CREATE MATERIALIZED VIEW mv_sales_report AS
SELECT
DATE_TRUNC('month', o.order_date)::date AS month,
p.category,
o.region,
COUNT(*) AS order_count,
COUNT(DISTINCT o.customer_id) AS unique_customers,
SUM(o.amount) AS total_revenue,
SUM(o.amount) FILTER (WHERE o.amount >= 50000) AS big_deal_revenue,
AVG(o.amount) AS avg_order_value,
SUM(oi.quantity * oi.unit_price) AS total_gmv
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
GROUP BY DATE_TRUNC('month', o.order_date), p.category, o.region
WITH DATA;
CREATE UNIQUE INDEX idx_mv_sales_report_pk
ON mv_sales_report (month, category, region);
CREATE VIEW v_sales_dashboard AS
SELECT
month,
region,
SUM(total_revenue) AS region_revenue,
SUM(order_count) AS region_orders,
SUM(unique_customers) AS region_customers
FROM mv_sales_report
GROUP BY month, region;
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_sales_report;
9. Fluxo de Execução
O fluxo de criação e atualização de uma view materializada:
flowchart TD
A[CREATE MATERIALIZED VIEW] --> B[Executar consulta subjacente]
B --> C[Gravar resultados em disco]
C --> D[Pode criar índices]
D --> E[Consulta lê dados do disco diretamente]
E --> F{Precisa atualizar?}
F -->|REFRESH| G[Reexecutar consulta subjacente]
G --> H[Substituir dados antigos]
H --> E
F -->|CONCURRENTLY| I[Atualização incremental por comparação]
I --> J[Não bloqueia leituras]
J --> E
style A fill:#e1f5fe
style E fill:#c8e6c9
style I fill:#fff9c4
| Passo | Descrição |
|---|---|
| Criar | Executar consulta subjacente, persistir resultado em disco |
| Consultar | Ler dados do disco diretamente, sem executar consulta subjacente |
| Atualizar | Reexecutar consulta subjacente, substituir dados antigos |
| CONCURRENTLY | Atualização incremental, não bloqueia consultas durante a atualização |
❓ Perguntas Frequentes
P: Views comuns afetam o desempenho? R: Não. Uma view é apenas reescrita de consulta—o desempenho é idêntico a escrever o SQL subjacente diretamente. Uma view complexa não adiciona sobrecarga extra.
P: Por que não consigo encontrar dados inseridos após inserir através de uma view? R: A linha inserida pode não satisfazer a condição WHERE da view. Use WITH CHECK OPTION para prevenir isso.
P: Por que a atualização CONCURRENTLY precisa de um índice único? R: CONCURRENTLY faz uma atualização incremental comparando dados antigos e novos; o índice único identifica cada linha. Sem ele, você recebe um erro.
P: Uma view materializada suporta UPDATE/DELETE direto? R: Não. Uma view materializada é somente leitura; só pode ser atualizada via REFRESH. Para alterá-la, atualize os dados da tabela base e depois faça REFRESH novamente.
P: Qual a diferença entre WITH CASCADED CHECK OPTION e WITH LOCAL CHECK OPTION? R: CASCADED verifica a condição da view atual e de todas as views dependentes; LOCAL verifica apenas a condição da view atual. Com views aninhadas, CASCADED é mais seguro.
P: Uma view materializada pode ser construída sobre outra view materializada? R: Sim. O PostgreSQL suporta views materializadas aninhadas, mas você deve atualizá-las manualmente na ordem de dependência.
📖 Resumo
- Uma view comum armazena apenas o texto da consulta, expande na consulta, dados sempre em tempo real
- Views simples (tabela única, sem agregação) são automaticamente atualizáveis, suportam INSERT/UPDATE/DELETE
- WITH CHECK OPTION previne que DML faça linhas escaparem do escopo da view
- Uma view materializada armazena o resultado da consulta em disco, extremamente rápida mas não em tempo real
- REFRESH MATERIALIZED VIEW faz atualização completa; CONCURRENTLY faz atualização incremental que não bloqueia leituras
- Pré-requisito para atualização CONCURRENTLY: a view materializada deve ter um índice único
- Views podem impor privilégios em nível de coluna, ocultando colunas sensíveis
📝 Exercícios
- ⭐ Crie uma view
v_recent_ordersque consulte pedidos dos últimos 30 dias, incluindocustomer_nameeproduct_name. - ⭐ Crie uma view atualizável
v_active_customersque mostre apenas clientes com status = 'active', e adicione WITH CHECK OPTION. - ⭐⭐ Crie uma view materializada
mv_daily_category_salesque resuma vendas por dia e categoria, e adicione um índice único para suportar atualização CONCURRENTLY. - ⭐⭐ Projete um esquema de view aninhada: uma view materializada pré-calcula dados detalhados, e uma view comum faz agregação dimensional sobre a view materializada.
- ⭐⭐⭐ Escreva uma tarefa programada pg_cron que atualize
mv_daily_category_salestodos os dias às 3:00 da manhã, e registre um alerta quando a atualização demorar mais de 5 minutos.