Performance – Caso Real – Parte1
Esta semana tivemos um problema de performance em uma rotina que estava funcionando normalmente e começou a apresentar lentidão chegando a exibir uma mensagem de timeout para o usuário. Comecei a analisar as query da procedure e encontrei o 1º problema. Segue um exemplo:
USE TempDB
GO
CREATE TABLE TesteA(ID_TesteA Int IDENTITY(1,1) PRIMARY KEY,
Data DateTime DEFAULT GETDATE())
CREATE TABLE TesteB(ID_TesteB Int IDENTITY(1,1),
Nome VarChar(200) DEFAULT NEWID(),
ID_TesteA Int REFERENCES TesteA(ID_TesteA),
Primary Key(ID_TesteB, Nome))
GO
SET NOCOUNT ON
DECLARE @I INT
— Insere 5000 linhas nas tabelas
SET @I = 0
WHILE @I < 5000
BEGIN
INSERT INTO TesteA DEFAULT VALUES
INSERT INTO TesteB(ID_TesteA) VALUES(@@IDENTITY)
SET @I = @I + 1;
END
GO
— Por causa de statisticas desatualizadas e o TOP + Order By DESC
— o SQL estava fazendo Scan na tabela TesteA
— No exemplo abaixo ele não irá fazer Scan, porem irá gerar um Sort
— por causa do TesteA.Data DESC. Como a tabela não está ordenada por Data e sim por
— ID_TesteA(Primary key e indice cluster) ele tem que ordenar a tabela por Data.
SELECT TOP 1 TesteA.Data
FROM TesteB
INNER JOIN TesteA
ON TesteB.ID_TesteA = TesteA.ID_TesteA
WHERE TesteB.ID_TesteB = 1
ORDER BY TesteA.Data DESC
GO
— A solução encontrada foi alterar a consulta acima para esta abaixo
— A idéia é pegar o ultimo registro incluido na tabela, portanto simplismente
— troquei o TOP 1 + Desc por MAX. No exemplo abaixo ele geou um Stream Aggregate na coluna
— TesteA.Data ordenando o resultado por TesteA.ID que é o indice Cluster e já está ordedado
— fisicamente.
SELECT MAX(TesteA.Data)
FROM TesteB
INNER JOIN TesteA
ON TesteB.ID_TesteA = TesteA.ID_TesteA
WHERE TesteB.ID_TesteB = 1
/*
O Edvaldo que trabalha junto comigo fez uma observação importante:
Cuidado em utilizar funções de agregação (MAX, MIN, COUNT…) com EXISTS pois sempre vai existir valor.
Exemplo:
IF EXISTS (SELECT MAX(campo) FROM tabela WHERE condição que não encontrou nenhum registro)
BEGIN
…
END
ELSE
BEGIN
…
END
Sempre vai passar no IF e nunca no ELSE.
*/
Continua…