Non search arguments no SQL Server
Algumas vezes o SQL Server não utiliza o plano de execução que você estava esperando e executa, por exemplo, um index scan onde era esperado um index seek. Um dos motivos desse comportamento pode ser a falta de índices (claro!), estatísticas desatualizadas ou problemas com a sua consulta, neste post vamos abordar um detalhe relacionado com o último problema.
O SQL Server possui o conceito de search arguments (SARG) que são condições da sua consulta que podem ser utilizadas para restringir o número de registros retornados para futura manipulação pelo plano de execução... Resumindo, é uma cláusula where que pode utilizar um índice para achar poucos registros (evitar um scan). O formato de uma SARG segue o padrão:
<Coluna> <operador_inclusivo> <constante ou variável>
ou
<constante ou variável> <operador_inclusivo> <Coluna>
O problema acontece quando utilizamos cláusulas negativas ou funções para definirmos nossos filtros (non SARG). Quando isso é feito a engine do SQL Server não utiliza os índices (mesmo que existentes e com estatísticas atualizadas) para fazer a consulta e trabalha com um table scan. PERFORMANCE!
Para demonstrar como isso funciona, montei um script com um exemplo baseado em um caso que encontrei nas andanças por esse Brasil. J
CREATE TABLE NonSARG
(
Data DATETIME NOT NULL,
Maquina VARCHAR(50) not null,
Numero decimal null
)
ALTER TABLE NonSARG
ADD CONSTRAINT pk_NonSARG
PRIMARY KEY (Data, Maquina)
GO
DECLARE @Contador INT
DECLARE @Data DATETIME
SET @Contador = 0
SET @Data = '20060404'
WHILE @Contador < 10000
BEGIN
INSERT INTO NonSarg VALUES (DATEADD(dd, @Contador, @Data), 'Maq' + CAST(@Contador AS VARCHAR(10)), 10)
set @contador = @contador + 1
END
Go
Esse script cria uma tabela chamada NonSARG e insere 10.000 registros, todos com datas únicas e nomes de máquinas únicas. No query analyzer habilite a opção para exibir o plano de execução (Ctrl + K) e execute a consulta:
select * from NonSARG WHERE Data = '20061229'
|--Clustered Index Seek(OBJECT:([Inside].[dbo].[NonSARG].[pk_NonSARG]), SEEK:([NonSARG].[Data]=Convert([@1])) ORDERED FORWARD)
Como resultado temos 1 registro que foi localizado através de um index seek utilizando o índice PK_NonSarg, como esperado. Porém quando executamos a próxima consulta, a nossa cláusula where deixou de ser um SARG, pois utilizamos a função CONVERT no campo data.
select * from NonSARG WHERE CONVERT(char,Data,111) = '2006/12/29'
|--Clustered Index Scan(OBJECT:([Inside].[dbo].[NonSARG].[pk_NonSARG]), WHERE:(Convert([NonSARG].[Data])='2006/12/29'))
Neste caso toda a tabela é percorrida até que seja encontrado o registro com a data 29/12/2006.
Em uma tabela de exemplo com somente 10.000 registros o impacto disto é muito pequeno, agora se você pegar uma tabela de mais de 10 milhões de registros, com vários campos (= menos registros por página no nível folha do índice clusterizado) e sendo acessada por vários processos, a coisa pode ficar feia.
Imagine agora a quantidade de locks exclusivos se for executado uma instrução do tipo delete ... where convert(...) = ‘2006/12/28’. Égua!! A coisa não fica bonita não.
Portanto na hora de você escrever as suas consultas, preste atenção como estão sendo construídos seus filtros e sempre verifique como é o plano de execução.
Para mais informações sobre isso, leia o Inside SQL Server 2000 J e o artigo https://www.sqlmag.com/articles/index.cfm?articleid=42349&puuid=67C1FAFF-1279-906B-AC6CEF8FB0E24437
Em anexo está o script com o exemplo.
[]s
Luti
Comments
- Anonymous
April 07, 2006
Nada técnico, apenas ri muiiito quando li o "égua" no meio do artigo, nem te entregastes! :P
abraços,
Ana Paula