Treinamento SQL Server

Módulo 12 — Views & Indexed Views

Simplificação de consultas, segurança, encapsulamento e materialização de agregações com indexed views.

T-SQL Nível Intermediário Hands-on Atualizado: 28 ago 2025

Objetivos

  • Compreender o que é uma VIEW e quando utilizá-la.
  • Criar views para simplificar relatórios e reforçar segurança.
  • Construir indexed views (materializadas): requisitos, criação e uso.
  • Aplicar boas práticas de performance (SARGability, NOEXPAND).

Pré-requisitos

  • Fundamentos de DDL/DML, Joins e Índices.
  • Módulo 11 (UDFs) recomendado.
  • Acesso a um banco de testes.

1) Introdução às Views

Uma VIEW é um objeto que salva uma consulta (um SELECT) com um nome. Ela não armazena dados (exceto quando indexada), mas fornece uma janela lógica para consultar dados.

Observação: Views comuns não têm parâmetros. Se precisar de parametrização, considere uma inline TVF (Módulo 11).

2) Sintaxe & Exemplos (CREATE / ALTER / DROP)

2.1) Exemplo básico

CREATE OR ALTER VIEW dbo.vw_ClientesAtivos
AS
SELECT IdCliente, Nome, Cidade, UF
FROM dbo.Clientes
WHERE Ativo = 1;
GO

-- Uso
SELECT * FROM dbo.vw_ClientesAtivos;

2.2) Junções e projeções

CREATE OR ALTER VIEW dbo.vw_VendasComCliente
AS
SELECT v.IdVenda, v.DataVenda, v.Valor,
       c.IdCliente, c.Nome, c.Cidade, c.UF
FROM dbo.Vendas AS v
JOIN dbo.Clientes AS c ON c.IdCliente = v.IdCliente;
GO

SELECT * FROM dbo.vw_VendasComCliente WHERE UF = 'SP';

2.3) Atualização através da View

Views simples (1 tabela, sem agregações, sem DISTINCT/TOP/UNION) tendem a ser atualizáveis:

-- Atualizando via view (exemplo simples)
UPDATE V
SET Cidade = N'Campinas'
FROM dbo.vw_ClientesAtivos AS V
WHERE V.IdCliente = 1;
Atenção: Views com junções/múltiplas tabelas podem não ser atualizáveis ou exigir INSTEAD OF TRIGGER para DML.

2.4) SCHEMABINDING (fixar schema)

CREATE OR ALTER VIEW dbo.vw_ClientesAtivos_SB
WITH SCHEMABINDING
AS
SELECT IdCliente, Nome, Cidade, UF
FROM dbo.Clientes;  -- <= nomes de 2 partes obrigatórios (schema.tabela)
GO
Com SCHEMABINDING: alterações de schema nas tabelas de base (ex.: DROP coluna) são bloqueadas enquanto a view existir.

3) Segurança & Encapsulamento

-- Exemplo de permissão
GRANT SELECT ON dbo.vw_ClientesAtivos TO RelatorioLeitura;

4) Views vs TVFs vs Procedures

RecursoViewTVF (inline)Procedure
ParâmetrosNãoSimSim
RetornoConjunto fixoTabela parametrizada0..N conjuntos/valores
Uso em JOINSimSim (via APPLY/JOIN)Não diretamente
MaterializaçãoNão (exceto indexed)NãoNão
DML diretoLimitadoNãoSim
DesempenhoConsulta salvaGeralmente ótimo (inline)Depende

5) Indexed Views (Materializadas)

Uma indexed view cria um índice clusterizado único sobre a view, materializando seus dados em disco (mantidos pelo SQL Server). Excelente para agregações ou junções frequentes.

5.1) Requisitos principais

5.2) SET options para criação/uso

Certas opções de sessão/compilação devem estar habilitadas tanto na criação quanto no uso da indexed view para que o otimizador a utilize:

SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET ARITHABORT ON;
SET NUMERIC_ROUNDABORT OFF;  -- deve estar OFF

5.3) Exemplo completo (agregação por cliente)

-- 1) View com SCHEMABINDING
CREATE OR ALTER VIEW dbo.vw_AggVendasPorCliente
WITH SCHEMABINDING
AS
SELECT
    c.IdCliente,
    COUNT_BIG(*)        AS QtdeVendas,
    SUM(v.Valor)        AS TotalVendido,
    MIN(v.DataVenda)    AS PrimeiraVenda,
    MAX(v.DataVenda)    AS UltimaVenda
FROM dbo.Vendas AS v
JOIN dbo.Clientes AS c ON c.IdCliente = v.IdCliente
GROUP BY c.IdCliente;
GO

-- 2) Índice clusterizado único (materializa a view)
CREATE UNIQUE CLUSTERED INDEX IXC_vw_AggVendasPorCliente_IdCliente
ON dbo.vw_AggVendasPorCliente (IdCliente);
GO

-- 3) Consulta que pode aproveitar a indexed view
-- (em alguns cenários, use NOEXPAND para garantir a utilização do índice da view)
SELECT IdCliente, TotalVendido
FROM dbo.vw_AggVendasPorCliente WITH (NOEXPAND)
WHERE TotalVendido >= 1000.00;
Quando usar: consultas repetitivas de agregação (por cliente, produto, mês etc.) em grandes volumes.
Custo de manutenção: DML nas tabelas base atualiza a view materializada. Avalie trade-offs leitura × escrita.

6) Desempenho & Otimizador

7) Laboratório Guiado

7.1) Setup (reaproveitando tabelas do Módulo 11)

-- Cria tabelas de exemplo se ainda não existirem
IF OBJECT_ID('dbo.Clientes','U') IS NULL
BEGIN
  CREATE TABLE dbo.Clientes
  (
      IdCliente      int            IDENTITY PRIMARY KEY,
      Nome           nvarchar(100)  NOT NULL,
      CPF            varchar(11)    NULL,
      DataNascimento date           NULL,
      Cidade         nvarchar(100)  NULL,
      UF             char(2)        NULL,
      Ativo          bit            NOT NULL DEFAULT 1
  );
END

IF OBJECT_ID('dbo.Vendas','U') IS NULL
BEGIN
  CREATE TABLE dbo.Vendas
  (
      IdVenda     int            IDENTITY PRIMARY KEY,
      IdCliente   int            NOT NULL REFERENCES dbo.Clientes(IdCliente),
      DataVenda   date           NOT NULL,
      Valor       decimal(10,2)  NOT NULL
  );
END

-- Dados mínimos (insere apenas se vazio)
IF NOT EXISTS (SELECT 1 FROM dbo.Clientes)
BEGIN
  INSERT INTO dbo.Clientes (Nome, CPF, DataNascimento, Cidade, UF, Ativo) VALUES
  (N'João Silva', '11122233344', '1993-05-10', N'São Paulo', 'SP', 1),
  (N'Ana Souza',  '55566677788', '1997-09-21', N'Campinas',  'SP', 1),
  (N'Bruno Lima', NULL,          '1985-12-02', N'Curitiba',  'PR', 0);
END

IF NOT EXISTS (SELECT 1 FROM dbo.Vendas)
BEGIN
  INSERT INTO dbo.Vendas (IdCliente, DataVenda, Valor) VALUES
  (1, '2025-08-01', 1200.00),
  (1, '2025-08-15',  350.50),
  (2, '2025-08-10',  999.90);
END
GO

7.2) View de relatório

CREATE OR ALTER VIEW dbo.vw_RelatorioVendas
AS
SELECT v.IdVenda, v.DataVenda, v.Valor,
       c.IdCliente, c.Nome, c.Cidade, c.UF, c.Ativo
FROM dbo.Vendas AS v
JOIN dbo.Clientes AS c ON c.IdCliente = v.IdCliente;
GO

SELECT * FROM dbo.vw_RelatorioVendas WHERE UF = 'SP';

7.3) Indexed view de agregação

-- Ative as SET options recomendadas antes:
SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON; SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON; SET CONCAT_NULL_YIELDS_NULL ON; SET ARITHABORT ON; SET NUMERIC_ROUNDABORT OFF;
GO

CREATE OR ALTER VIEW dbo.vw_SalesAggByCliente
WITH SCHEMABINDING
AS
SELECT
    c.IdCliente,
    COUNT_BIG(*)     AS Cnt,
    SUM(v.Valor)     AS Total,
    MIN(v.DataVenda) AS FirstSale,
    MAX(v.DataVenda) AS LastSale
FROM dbo.Vendas AS v
JOIN dbo.Clientes AS c ON c.IdCliente = v.IdCliente
GROUP BY c.IdCliente;
GO

-- Índice clusterizado único para materializar
CREATE UNIQUE CLUSTERED INDEX IXC_vw_SalesAggByCliente_IdCliente
ON dbo.vw_SalesAggByCliente (IdCliente);
GO

-- Consulta usando NOEXPAND
SELECT IdCliente, Total
FROM dbo.vw_SalesAggByCliente WITH (NOEXPAND)
WHERE Total >= 1000.00;

7.4) Medindo impacto

SET STATISTICS IO ON; SET STATISTICS TIME ON;
GO

-- Consulta agregada diretamente nas tabelas
SELECT v.IdCliente, SUM(v.Valor) AS Total
FROM dbo.Vendas AS v
GROUP BY v.IdCliente
HAVING SUM(v.Valor) >= 1000.00;
GO

-- Consulta contra a indexed view
SELECT IdCliente, Total
FROM dbo.vw_SalesAggByCliente WITH (NOEXPAND)
WHERE Total >= 1000.00;
GO

SET STATISTICS IO OFF; SET STATISTICS TIME OFF;

8) Exercícios

  1. View de segurança: Crie dbo.vw_ClientesPublico expondo apenas IdCliente, Nome, UF de clientes ativos e conceda GRANT SELECT a um usuário fictício.
  2. View de relatório: Crie dbo.vw_VendasSP listando vendas apenas de clientes de SP (join + filtro).
  3. Indexed view simples: Crie uma view agregando SUM(Valor) por UF com COUNT_BIG(*), SCHEMABINDING e índice clusterizado único.
  4. NOEXPAND: Escreva uma consulta que utilize WITH (NOEXPAND) para buscar clientes com Total > 2000,00 na indexed view criada.
  5. SET options: Mostre os comandos necessários de SET para permitir o uso da indexed view durante a execução de uma consulta.

9) Gabarito (sugestões)

-- 1) View de segurança
CREATE OR ALTER VIEW dbo.vw_ClientesPublico
AS
SELECT IdCliente, Nome, UF
FROM dbo.Clientes
WHERE Ativo = 1;
GO
GRANT SELECT ON dbo.vw_ClientesPublico TO UsuarioRelatorio;
GO

-- 2) View de relatório SP
CREATE OR ALTER VIEW dbo.vw_VendasSP
AS
SELECT v.IdVenda, v.DataVenda, v.Valor, c.IdCliente, c.Nome
FROM dbo.Vendas AS v
JOIN dbo.Clientes AS c ON c.IdCliente = v.IdCliente
WHERE c.UF = 'SP';
GO

-- 3) Indexed view por UF
SET ANSI_NULLS ON; SET QUOTED_IDENTIFIER ON; SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON; SET CONCAT_NULL_YIELDS_NULL ON; SET ARITHABORT ON; SET NUMERIC_ROUNDABORT OFF;
GO
CREATE OR ALTER VIEW dbo.vw_AggVendasPorUF
WITH SCHEMABINDING
AS
SELECT c.UF,
       COUNT_BIG(*) AS Qtde,
       SUM(v.Valor) AS TotalUF
FROM dbo.Vendas AS v
JOIN dbo.Clientes AS c ON c.IdCliente = v.IdCliente
GROUP BY c.UF;
GO
CREATE UNIQUE CLUSTERED INDEX IXC_vw_AggVendasPorUF_UF ON dbo.vw_AggVendasPorUF (UF);
GO

-- 4) NOEXPAND
SELECT UF, TotalUF
FROM dbo.vw_AggVendasPorUF WITH (NOEXPAND)
WHERE TotalUF > 2000.00;

-- 5) SET options
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET ARITHABORT ON;
SET NUMERIC_ROUNDABORT OFF;

10) Erros Comuns & Checklist

Erros Comuns

  • Esperar parâmetros em views comuns (não suportado).
  • Esquecer SCHEMABINDING e nomes em 2 partes ao criar indexed view.
  • Usar funções não-determinísticas (GETDATE(), NEWID()) na indexed view.
  • Incluir OUTER JOIN, TOP, UNION na indexed view.
  • Ignorar os SET options obrigatórios.
  • Não analisar o custo de manutenção de DML nas tabelas base.

Checklist Rápido

  • A view resolve um padrão de consulta real do time?
  • Há ganhos claros de segurança/organização?
  • Para materializar: requisitos de indexed view atendidos?
  • Índice clusterizado único definido com chave correta?
  • Consulta aproveita a indexed view (talvez com NOEXPAND)?

11) Próximos Passos