Simplificação de consultas, segurança, encapsulamento e materialização de agregações com indexed 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.
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;
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';
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;
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
GRANT diretamente na view (nega acesso às tabelas base).UF) criando views específicas ou usando RLS (fora do escopo aqui).-- Exemplo de permissão
GRANT SELECT ON dbo.vw_ClientesAtivos TO RelatorioLeitura;
| Recurso | View | TVF (inline) | Procedure |
|---|---|---|---|
| Parâmetros | Não | Sim | Sim |
| Retorno | Conjunto fixo | Tabela parametrizada | 0..N conjuntos/valores |
| Uso em JOIN | Sim | Sim (via APPLY/JOIN) | Não diretamente |
| Materialização | Não (exceto indexed) | Não | Não |
| DML direto | Limitado | Não | Sim |
| Desempenho | Consulta salva | Geralmente ótimo (inline) | Depende |
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.
WITH SCHEMABINDING na definição da view.GETDATE(), NEWID(), RAND()).TOP, TEXT/NTEXT/IMAGE, OUTER JOIN, UNION, SUBQUERY em SELECT da view.COUNT_BIG(*) e liste todas as colunas do GROUP BY no índice.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
-- 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;
WITH (NOEXPAND) quando necessário para forçar a não expansão da view e uso do índice materializado.SET STATISTICS IO, TIME ON e compare com/sem a indexed view.-- 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
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';
-- 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;
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;
dbo.vw_ClientesPublico expondo apenas IdCliente, Nome, UF de clientes ativos e conceda GRANT SELECT a um usuário fictício.dbo.vw_VendasSP listando vendas apenas de clientes de SP (join + filtro).SUM(Valor) por UF com COUNT_BIG(*), SCHEMABINDING e índice clusterizado único.WITH (NOEXPAND) para buscar clientes com Total > 2000,00 na indexed view criada.-- 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;
SCHEMABINDING e nomes em 2 partes ao criar indexed view.GETDATE(), NEWID()) na indexed view.OUTER JOIN, TOP, UNION na indexed view.NOEXPAND)?