Cuidado com Views…
Pessoal já faz um tempo que estou sem blogar né?… Bom mesmo sem escrever para o blog por um tempo, isso não significa que fiquei sem escrever sobre SQL… na verdade bastante coisa aconteceu desde meu último post. Vamos as novidades:
· Tenho escrito bastante para o Simple Talk, a cada semana escrevo sobre um operador dos planos de execução, se você ainda não viu, segue o link para os artigos http://www.simple-talk.com/author/fabiano-amorim/… Ainda tem muito pela frente e eu estou adorando J, espero que eu consiga manter a meta de 1 por semana.
· O tão falado Marília Tech Day foi realmente um sucesso, foi uma honra pra mim poder participar e logo estarei no Prudente TechDay…
Tenho as gravações de minhas palestras e a palestra do Fabricio Catae. Eu fiz duas sessões uma sobre Joins no SQL Server e outra sobre índexação. Acabei sendo pego de surpresa pois o nosso amigo Rodrigo Fernandes teve um imprevisto e não conseguiu pegar o Vôo para o evento, então aproveitei o horário dele e falei sobre Índices no SQL Server, eu achei que ficou legal com bastante exemplos e boas perguntas da galera.
Ainda estou brigando para achar um programa decente para edição dos vídeos, assim que eu conseguir vou subir pro YouTube e coloco os links aqui no blog.
· Eu já havia sido convidado para falar sobre SQL Server em uma faculdade daqui de Tupã, então logo após o Marília TechDay falei sobre Certificações e SQL Server 2008 aqui na FACAT. Foi bem legal, é sempre bom ver a galera entusiasmada com as funcionalidades do SQL J.
Bom acabando a conversa fiada, vamos para o que interessa mais SQL Server.
Eu sempre me surpreendo com as situações que encontro no dia a dia… Hoje eu estava trabalhando em uma procedure que lia alguns dados a partir de uma view, e a consulta não utilizava um determinado índice de jeito nenhum. Fui olhar com calma e encontrei o seguinte cenário.
Vamos criar umas tabelas de teste para entender o problema.
IF OBJECT_ID(‘Pedidos’) IS NOT NULL
BEGIN
DROP TABLE Clientes
DROP TABLE Pedidos
END
GO
CREATE TABLE Pedidos(ID_Pedido Integer Identity(1,1),
ID_Cliente Int,
Data_Pedido DateTime,
Valor Numeric(18,2),
CONSTRAINT xpk_Pedidos PRIMARY KEY (ID_Pedido))
GO
CREATE TABLE Clientes(ID_Cliente Integer Identity(1,1),
Nome_Cliente VarChar(300)
CONSTRAINT xpk_Clientes PRIMARY KEY (ID_Cliente))
GO
CREATE INDEX ix_teste ON Clientes(Nome_Cliente)
GO
DECLARE @i Integer
SET @i = 0
WHILE @i < 10000
BEGIN
INSERT INTO Pedidos(ID_Cliente,
Data_Pedido,
Valor)
VALUES(ABS(CheckSum(NEWID()) / 10000000),
GetDate() – ABS(CheckSum(NEWID()) / 10000000),
ABS(CheckSum(NEWID()) / 1000000))
INSERT INTO Clientes(Nome_Cliente)
VALUES (NEWID())
SET @i = @i + 1
END
GO
SELECT * FROM Pedidos
SELECT * FROM Clientes
Segue um exemplo dos dados incluidos
Tenho uma tabela simples de pedidos e os dados de clientes, agora vou criar uma view que me retorna os dados dos pedidos com o nome do cliente.
CREATE VIEW vw_Teste
AS SELECT Pedidos.*, ISNULL(Clientes.Nome_Cliente,’Não Existe’) AS Nome_Cliente
FROM Pedidos
LEFT OUTER JOIN Clientes
ON Clientes.ID_Cliente = Pedidos.ID_Cliente
Sim, eu fiz o LEFT OUTER JOIN de propósito pois pode acontecer de um registro da Pedidos.ID_Cliente não estar na tabela de Clientes. Estou apenas usando um cenário para exemplo.
Existe um índice por Clientes.Nome_Cliente, vamos ver ele na prática. A consulta abaixo utiliza o mesmo select da view porém desta vez estamos fazendo um filtro por nome_cliente.
SELECT Pedidos.*, ISNULL(Clientes.Nome_Cliente,’Não Existe’) AS Nome_Cliente
FROM Pedidos
LEFT OUTER JOIN Clientes
ON Clientes.ID_Cliente = Pedidos.ID_Cliente
WHERE Clientes.Nome_Cliente like ‘A%’
Como podemos ver, o plano acima utilizou o índice ix_teste para filtrar os dados
Porem se efetuarmos o mesmo select utilizando a view temos o seguinte plano.
SELECT * FROM vw_Teste
WHERE Nome_Cliente like ‘A%’
Bem, a esta altura você já deve ter identificado qual é o problema. Mas pra quem ainda não pegou, fica a pergunta, porque o SQL faz o seek quando uso o código da view mas quando uso a view ele não faz o seek?
O que acontece é que na view estamos alterando o valor da coluna Nome_Cliente, repare que estou utilizando a function ISNULL para validar se os dados foram retornados, e isso impede que o SQL utilize o índice.
Para resolver este problema, poderiamos retornar duas colunas na view uma com o ISNULL e outra sem o ISNULL efetuar nenhuma alteração nela. Outra forma seria incluir outra coluna que faz parte da tabela Clientes no resultado da view e utilizar esta coluna.
Bom fica a dica.
Abraços