Inicial > Não categorizado > SQL – Query Optimizer – Updates e NonClustered Index

SQL – Query Optimizer – Updates e NonClustered Index

Query Optimizer – Unique Index vs Selects e Updates
Autor – Fabiano Neves Amorim

Início

Dando continuidade ao tema Query Optmizer, neste artigo veremos mais detalhes sobre o comportamento interno da criação de um plano de execução, quanto a Unique Index, Selects e Updates.

É importante que os desenvolvedores saibam como o SQL executa uma consulta para assim escrever um código eficiente. Para os DBAs este conhecimento é ainda mais importante. Ao trabalhar com banco de dados, é fundamental que você entenda o comportamento do QO (Query Optimizer).

Como analogia, imagine duas empresas de construção. Ambas vão construir uma casa. Para isso, a primeira empresa começa planejando a construção, analisando qual será a ordem das tarefas, quais tarefas podem ser feitas em paralelo, qual depende da outra, e qual é o melhor trabalhador para efetuar cada tarefa. A segunda empresa não faz o planejamento e já começa a construção.

É bem provável que a empresa que fez o planejamento termine a construção primeiro e a segunda empresa nem termine. Construir uma casa não é uma tarefa simples e requer um planejamento. De forma semelhante, um banco de dados relacional requer que suas consultas sejam analisadas e planejadas antes de serem desenvolvidas. Quanto mais complexa a operação, maior é o valor do planejamento.

Um pouco mais de Query Optimizer

Na matéria sobre Query Processor (QP) publicada na edição 65 da SQL Magazine, vimos que o Query Optimizer (QO) executa uma série de passos para gerar um plano de execução de uma consulta. Uma dessas fases é conhecida como Parse e Normalization (Bind).

O Parse analisa as características dos objetos referenciados nas consultas e valida a sintaxe do comando para ver se ele é lógico e apto para execução. O parse gera uma Sequence Tree, também conhecido como Query Processor Tree, que é uma representação lógica dos passos necessários para a execução do comando SQL.

O Sequence Tree é passado para o Normalization, que efetua a tarefa de Bind. Durante esta etapa, o SQL verifica se as tabelas e colunas existem e lê as informações sobre os objetos acessando o metadata do SQL. Nesta etapa também são efetuadas conversões implícitas, por exemplo: se está sendo realizado um insert de uma string “50.30” em uma coluna do tipo Numeric, o SQL efetua uma conversão implícita desta string para o tipo adequado.

Preparando o ambiente

Para melhorar o entendimento dos exemplos, utilizaremos uma tabela com dados aleatórios que servirão como base para os testes que serão apresentados. A Listagem 1 contém o script para criação destes objetos. Este script cria uma tabela chamada TabTeste, que contém alguns campos e, em seguida, são criados quatro índices, dois unique index e dois nonclustered index. Logo após, cinco registros são inseridos com dados aleatórios.

CREATE TABLE TabTeste (ID Int IDENTITY(1,1) PRIMARY KEY,

Nome VarChar(250) NULL,

Nome2 VarChar(250) NULL,

Val Int NULL,

Val2 Int NULL)

GO

CREATE UNIQUE INDEX ix_Unique ON TabTeste(Nome)

CREATE INDEX ix_NonUnique ON TabTeste(Nome2)

CREATE UNIQUE INDEX ix_UniqueVal ON TabTeste(Val)

CREATE INDEX ix_NonUniqueVal2 ON TabTeste(Val2)

— Massa aleatoria de dados.

INSERT INTO TabTeste(Nome, Val) VALUES(NEWID(), 1)

INSERT INTO TabTeste(Nome, Val) VALUES(NEWID(), 2)

INSERT INTO TabTeste(Nome, Val) VALUES(NEWID(), 3)

INSERT INTO TabTeste(Nome, Val) VALUES(NEWID(), 4)

INSERT INTO TabTeste(Nome, Val) VALUES(NEWID(), 5)

GO

UPDATE TabTeste SET Nome2 = Nome, Val2 = Val

Listagem 1. Script para criação dos objetos de teste

Select e Unique Index

Unique Index é um tipo de índice que pode ser criado sobre uma coluna de uma tabela, onde você deseja garantir que seu valor não pode ser duplicado. Em índices únicos, todas as linhas da tabela terão um valor único para cada registro.

Sempre que você criar um índice e ele for um Unique Index, isto é, se você deseja que o SQL crie um controle para que os valores da coluna não dupliquem, pense nos prós e contras da utilização do Unique Index. Esta informação será de grande valor para o QO decidir se o índice será utilizado ou não, visto que quando o SQL tiver que ler algum valor neste índice ele saberá que este valor é sempre único – o que representa um alto nível de seletividade (vide Nota 1). Quanto mais seletiva for uma coluna, maior a possibilidade de utilização de um índice que pertence a esta coluna.

Vamos utilizar como exemplo a tabela TabTeste, onde temos cinco campos e 4 índices. Dois Unique Index por Nome e Val, e outros dois NonClustered Index por Nome2 e Val2 respectivamente. O Nonclustered Index, ao contrário do Unique Index, permite que os valores sejam duplicados.

Na Listagem 2 temos duas simples consultas que retornam os dados da tabela TabTeste. A primeira utiliza um filtro na coluna Nome, que contém um Unique Index, e a segunda utiliza o mesmo filtro, porém, utilizando a coluna Nome2, que possui um NonClustered Index.

SELECT * FROM TabTeste

WHERE Nome = ‘28198490-10E0-44A2-A331-863242BA717E’

SELECT * FROM TabTeste

WHERE Nome2 = ‘28198490-10E0-44A2-A331-863242BA717E’

Listagem 2. Selects exemplo.

clip_image002

Figura 1. Planos de execução gerados pelo select da Listagem 2.

Antes de analisarmos os planos de execução da Figura 1, vamos entender alguns termos importantes.

Sempre que um índice é criado, o SQL Server pega uma série de informações em relação à coluna informada no índice e grava estes dados em uma área de armazenamento no banco. Estes dados são conhecidos por estatísticas.

As estatísticas fornecem informações muito importantes para o QO. Dentre elas podemos destacar o nível de seletividade, densidade e cardinalidade (vide nota) de uma coluna. Conforme novos registros são incluídos ou apagados, automaticamente o SQL Server tenta manter as estatísticas atualizadas para trabalhar com valores atualizados.

É possível configurar o SQL para que ele não atualize as estatísticas automaticamente. Para isso, basta acessar as propriedades do banco de dados e definir a propriedade Auto Update Statistics como false. É importante destacar que caso seja utilizada esta opção (false), será de extrema importância que você tenha uma rotina de manutenção das estatísticas de seu banco. Estatísticas desatualizadas podem acarretar em planos de execução ineficientes.

Voltando aos planos da Figura 1, como podemos observar, para o primeiro select o SQL gerou um index Seek e o plano todo representa 31% do custo das duas consultas. Neste caso podemos perceber que o primeiro select é mais eficiente que a segunda consulta, que representa 69% do custo total de execução. Ainda que as estatísticas de sua tabela estejam desatualizadas, o SQL sabe que a seletividade dos dados na coluna Nome é altíssima, já que ela sempre irá retornar um registro devido a presença do unique index.

Já a segunda consulta, que faz o select baseado no where pela coluna Nome2, gerou um Clustered Index Scan, ou seja, o SQL irá varrer toda a tabela procurando pelos registros que satisfaçam o SARG (vide nota) especificado no where. Neste caso as estatísticas não forneceram informações precisas. O QO não conseguiu estimar a quantidade de linhas que seriam retornadas pelo comando, processo também conhecido por cardinalidade (vide nota).

Updates e Nonclustered Index

Ao criar um Unique Index devemos considerar a complexidade de um simples UPDATE ou INSERT, pois o SQL tem que garantir que nenhum valor será duplicado e, para isso ele utiliza alguns mecanismos muito importantes. Vamos analisá-los progressivamente; primeiro com um simples update em uma tabela atualizando uma coluna que não possui índice único (Listagem 3) e, posteriormente, em coluna que possui um índice único.

UPDATE TabTeste SET Val2 = Val2 + 1

Listagem 3. Update na tabela TabTeste em uma coluna que não possui índice único.

clip_image004

Figura 2. Planos de execução gerados pelo update da Listagem 3.

Na Figura 2 temos o plano de execução gerado pelo comando de update da Listagem 3. Na Listagem 4 podemos observar o plano de execução no modo texto usando o SET SHOWPLAN_TEXT ON antes de rodar o código da Listagem 3.

UPDATE [TabTeste] set [Val2] = [Val2]+@1

|–Clustered Index Update(OBJECT:([TabTeste].[PK__TabTeste__3214EC2740DB41A9]), OBJECT:([TabTeste].[ix_NonUniqueVal2]), SET:([TabTeste].[Val2] = [Expr1003]))

|–Compute Scalar(DEFINE:([Expr1016]=[Expr1016]))

|–Compute Scalar(DEFINE:([Expr1016]=CASE WHEN [Expr1007] THEN (0) ELSE (1) END))

|–Compute Scalar(DEFINE:([Expr1003]=[TabTeste].[Val2]+(1), [Expr1007]=CASE WHEN [TabTeste].[Val2] = ([TabTeste].[Val2]+(1)) THEN (1) ELSE (0) END))

|–Top(ROWCOUNT est 0)

|–Clustered Index Scan(OBJECT:([TabTeste].[PK__TabTeste__3214EC2740DB41A9]))

Listagem 4. Plano de execução em Modo Texto.

Todos os comandos de Update são executados em dois passos principais, o primeiro é o de leitura dos registros que serão atualizados, e o segundo é o de escrita dos dados que sofrerão o Update. Vamos detalhar e analisar cada passo gerado pelo plano de execução (Listagem 4):

.

· clip_image005 Clustered Index Scan: Etapa onde o SQL lê todos os dados que serão atualizados pelo UPDATE. Este é o primeiro passo que mencionei acima, o de leitura;

· clip_image006 TOP: Se você analisar o plano de execução usando o modo texto (Listagem 4), verá que foi gerado um comando Top (ROWCOUNT est 0), este Top é utilizado para implementar o SET ROWCOUNT, e “est” é uma abreviação de estimate. Esta implementação é necessária para garantir que o número de linhas atualizadas seja exatamente o número desejado.

Repare que se executarmos um SET ROWCOUNT 1 antes do update, o SQL irá executar um comando Top(ROWCOUNT est 1). Ex:

SET ROWCOUNT 1

UPDATE TabTeste SET Val2 = Val2 + 1

OPTION(RECOMPILE)

Listagem 5. Update utilizando SET ROWCOUNT 1.

|–Top(ROWCOUNT est 1)

Listagem 6. Código do plano onde é implementado o TOP (ROWCOUNT est 1)

Na listagem 5 o update utiliza o hint RECOMPILE para forçar a geração de um novo do plano de execução, a fim de vermos a alteração no TOP exibida na Listagem 6.

· clip_image007 Compute Scalar: Nesta etapa temos algumas considerações bem interessantes, e uma novidade que foi implementada no SQL Server 2005. Vamos rever parte do código gerado pelo plano de execução em modo Texto:

CASE

WHEN [TabTeste].[Val2] = [TabTeste].[Val2]+(1)) THEN (1)

ELSE (0)

END

Listagem 7. Comando Case.

o Com o código um pouco mais claro, vamos entender o que está acontecendo aqui:

§ O SQL valida se o valor que será atualizado (Val2 + 1) é igual ao valor da coluna Val2 no índice nonclustered (ix_NonUniqueVal2) afetado, opa, espera ai! Para tudo! Quer dizer que se o valor atual no índice nonclustered, for igual ao novo valor que foi passado para o SQL, ele não perde tempo atualizando esta linha?

Sim, é isso mesmo, é claro que existe um overhead em fazer essa validação, mas com certeza vale à pena verificar se ouve alguma alteração antes se dar o trabalho de sair trocando 6 por meia dúzia.

· Esta validação é importante porque no caso de updates em colunas que pertencem ao um índice nonclustered, o SQL não efetua apenas um update no valor. Internamente o SQL exclui a linha do índice, para depois fazer um insert do novo valor. Os passos para a atualização representada pela listagem 3 seriam:

o Localizar os valores que serão atualizados utilizando o índice cluster.

o Atualizar os valores no índice cluster.

o Efetuar um delete no valor antigo do índice nonclustered.

o Inserir o novo valor no índice nonclustered.

§ Continuando a analisar o Case, caso o valor de Val2 seja igual ao valor de Val2 +1(informação fornecida no “SET Val2 =”) então retorna 1 (nada mudou) senão retorna 0 (valor mudou).

§ Existe uma DMV (sempre elas) que podemos utilizar para saber se o SQL teve que atualizar o valor de um índice nonclustered ou não. Esta DMV é a sys.dm_db_index_operational_stats(vide nota), você pode analisar a coluna leaf_insert_count para verificar se ouve um novo insert no seu índice nonclustered quando o SQL executou o comando de update. Lembra que falei que na verdade ele sofre um delete mais um insert? É por isso você deverá analisar a coluna leaf_insert_count, e não a leaf_update_count. Somente no caso de um índice cluster que o valor alterado é exibido na leaf_update_count.

§ Repetindo, isso só foi implementado no SQL Server 2005. O update da listagem 3 compilado no SQL 2000 gera o seguinte Execution Plan:

clip_image009

Figura 3. Planos de execução gerados pelo update no SQL Server 2000.

UPDATE [TabTeste] SET [Val2]=[Val2]+@1

|–Clustered Index Update(OBJECT:([TabTeste].[PK__TabTeste__32826724]), SET:([TabTeste].[Val2]=[Expr1004]))

|–Compute Scalar(DEFINE:([Expr1004]=[TabTeste].[Val2]+1))

|–Top(ROWCOUNT est 0)

|–Clustered Index Scan(OBJECT:([TabTeste].[PK__TabTeste__32826724]))

Listagem 8. Plano em modo texto.

· Conforme pode ser observado na listagem 8, não existe o operador compute scalar com o case. Realmente é uma pena que a maioria dos usuários de SQL Server no Brasil ainda utilizam a versão 2000.

· clip_image007[1] Compute Scalar: Este outro operador de compute scalar é gerado para cálculos internos do SQL Server.

· clip_image010 Clustered Index Update:

o Este é o segundo passo dos dois passos necessários para executar um update conforme mencionei acima. Aqui é onde o SQL efetua o update nos registros lidos pelo Clustered Index Scan. Como existe um índice cluster na coluna afetada pelo update, o SQL gerou apenas um operador que atualiza os dois índices. São eles: o índice cluster que contem os dados da tabela, e o índice nonclustered (ix_NonUniqueVal2).

Este tipo de plano de execução, que usa o mesmo operador para atualizar dois lugares diferentes, também é conhecido como “Narrow Plan” ou “Per-Row Plan”.

Repare no plano de execução em modo texto exibido na listagem 4 que, os nomes dos dois índices aparecem no operador de Clustered Index Update, ou seja, ambos serão atualizados pelo mesmo operador.

No SQL Server 2000 não é exibida a informação de todos os índices que serão atualizados, embora ele atualize os 2 índices assim como no SQL 2005, este comportamento de exibir todos os índices no plano de execução só foi implementado na versão 2005.

o Sempre que acontece um update em um campo que pertence a um índice nonclustered, o SQL tem que atualizar esta informação na tabela e no índice nonclustered.

Normalmente (existem exceções, veremos uma delas mais abaixo), quando o SQL vai atualizar o valor em uma tabela, ele executa esta operação por ordem da “chave” do índice cluster, e não na ordem da chave dos índices nonclustered.

Isso significa que para cada linha atualizada no índice cluster, o SQL precisa fazer um seek (buscar o valor correspondente) no índice nonclustered para atualizar seu valor. Quando uma grande quantidade de linhas é atualizada este processo de leitura desordenada (visto que está ordenado pela chave do cluster) no índice nonclustered acaba sendo bastante custoso para o SQL Server.

Quando isso acontece, o QO pode optar por usar outro meio para atualizar os dados nos índices nonclustered. Neste caso ele gera um index update ordenado para cada chave do índex nonclustered afetado no update.

O QO escolhe usar esta opção baseado na quantidade de linhas que serão afetadas pelo update e, a quantidade de índices nonclustered que serão atualizados. Existem ainda outras medidas que são levadas em consideração antes de o QO tomar esta decisão, porem as principais são essas.

Este comportamento de gerar um plano de execução com um Index Update para atualizar cada índice envolvido na consulta, é conhecido como “Wide Plan”.

Updates e Unique Index

Depois que analisamos o comportamento de um simples update atualizando uma coluna que não tem um Unique Index, vamos ver o que acontece quando efetuamos outro update, desta vez atualizando uma coluna que possui (Listagem 9).

UPDATE TabTeste SET Val = Val + 1

Listagem 9. Update na tabela TabTeste atualizando uma coluna que contem um índice único.

clip_image012

Figura 4. Plano de execução gerado por um update em uma coluna com unique index.

Na Listagem 10 podemos observar o plano de execução no modo texto usando o SET SHOWPLAN_TEXT ON antes de rodar o código da Listagem 9.

UPDATE [TabTeste] set [Val] = [Val]+@1

|–Index Update(OBJECT:([TabTeste].[ix_UniqueVal]), SET:(Insert, [ID1022] = [TabTeste].[ID],[Val1023] = [TabTeste].[Val]), SET:(Update, [ID1022] = RaiseIfNullUpdate([TabTeste].[ID])) ACTION:([Act1021]))

|–Collapse(GROUP BY:([TabTeste].[Val]))

|–Sort(ORDER BY:([TabTeste].[Val] ASC, [Act1021] ASC))

|–Filter(WHERE:(NOT [Expr1019]))

|–Split

|–Clustered Index Update(OBJECT:([TabTeste].[PK__TabTeste__3214EC2765D7BC2E]), SET:([TabTeste].[Val] = [Expr1003]))

|–Compute Scalar(DEFINE:([Expr1019]=[Expr1019]))

|–Compute Scalar(DEFINE:([Expr1019]=CASE WHEN [Expr1007] THEN (1) ELSE (0) END))

|–Compute Scalar(DEFINE:([Expr1003]=[TabTeste].[Val]+(1), [Expr1007]=CASE WHEN [TabTeste].[Val] = ([TabTeste].[Val]+(1)) THEN (1) ELSE (0) END))

|–Top(ROWCOUNT est 0)

|–Clustered Index Scan(OBJECT:([TabTeste].[PK__TabTeste__3214EC2765D7BC2E]))

Listagem 10. Plano gerado pelo Update da listagem 9.

Agora o plano de consulta parece ter ficado um pouco mais complicado, ou podemos dizer interessante?

Podemos observar que do operador Clustered Index Scan até o Clustered Index Update o SQL gerou exatamente o mesmo plano de execução do primeiro Update que analisamos. Existe apenas a exceção que desta vez o Clustered Index Update não irá atualizar o índice cluster e o nonclustered, atualizará somente o cluster.

Depois do Clustered Index Update temos alguns operadores novos em relação ao update da Listagem 3, são eles: Split, Filter, Sort, Collpase e por fim o Index Update, que será responsável por atualizar, ou melhor, apagar e incluir o novo valor no nonclustered unique index ix_UniqueVal.

O que acontece aqui é que o SQL precisa garantir que o valor que será atualizado não viole a regra de unique. Não pode haver nenhum valor com o mesmo valor que está tentando ser inserido. Fazer este controle pode parecer uma tarefa simples, mas para isso o SQL utiliza alguns operadores específicos, vamos analisar melhor cada um deles.

Imagine que os dados da tabela estejam conforme a Figura 5.

clip_image014

Figura 5. Dados tabela TabTeste.

Digamos que o SQL opte por usar o mesmo plano de execução gerado pelo primeiro update, aquele sem o Split, exibido na Figura 2. Lembra que vimos que o SQL efetua o update na ordem do índice cluster (coluna ID)? Pois é, seguindo esta regra o primeiro registro a ser atualizado seria o ID = 1. Vamos seguir na lógica, localiza o registro no índice nonclustered passando como chave o Val (1). Efetua um delete, e depois efetua um insert com o valor atualizado(Val (1) + 1 = 2), porém o valor 2 já existe (ID = 2), o que causaria um falso erro de violação de unicidade. Este erro é caracterizado por falso, pois o valor 2 posteriormente também seria atualizado para Val(2) + 1 = 3, o que viabilizaria o insert com o valor 2.

Para evitar este tipo de problema, o QO automaticamente escolhe utilizar os operadores Split, Sort e Collapse para fazer o update por ordem da coluna do índice nonclustered, no nosso exemplo, a coluna Val.

Vamos analisar melhor estes operadores:

· clip_image015 Split: O operador de Split será responsável por transformar os updates em deletes seguidos de inserts;

· clip_image016 Sort: O sort irá reordenar os inserts e deletes para que eles sejam executados ordenados pela coluna do nonclustered unique index;

· clip_image017 Filter: Efetua um filtro para selecionar apenas as colunas onde o operador Compute Scalar com o case gerou um valor = 0 ou seja, valores que sofreram alteração;

· clip_image018 Collapse: Agrupa todos os inserts e deletes que utilizam o mesmo valor da chave do índice cluster. Se o SQL encontrar algum insert ou delete para uma mesma chave ele agrupa todos para um mesmo update para deixar a operação mais eficiente. Após este agrupamento o collapse passa os updates que serão executados para o Index Update;

· clip_image010[1] Index Update: Atualiza o índice com o update gerado pelo Collapse.

É importante destacar que mesmo com estes mecanismos para evitar que uma coluna tenha o valor duplicado, ainda assim pode acontecer de um valor repetir. Quando isso acontecer o SQL irá gerar uma exceção. Este comportamento apenas evita que falsas violações ocorram.

No caso de um unique índex:

· Uma violação ocorre quando um valor que já existe na coluna está sendo inserido novamente;

· Uma falsa violação ocorre quando uma coluna está recebendo um valor que já existe, mas que durante o processo de atualização dos dados, este valor já existente também será atualizado. Daí o termo, falsa violação. Ela é falsa pois no final do update a coluna não terá nenhum valor repetido.

O QO também é esperto o suficiente para identificar quando não tem como um update gerar uma falsa violação. Na Listagem 11 podemos ver alguns exemplos.

UPDATE TabTeste SET Val = 1

UPDATE TOP(1) TabTeste SET Val = Val + 1

UPDATE TabTeste SET Val = Val + 1 WHERE ID = 1

Listagem 11. Updates que não geram violação.

Por causa da presença do unique índex, nas três consultas da Listagem 11 o QO sabe que somente uma linha poderá ser atualizada com o valor 1.

No primeiro update, se a tabela tiver mais de um registro o SQL irá gerar uma exceção, pois neste caso o unique index obriga que os valores não repitam. Não há necessidade de prever uma possível falsa violação, pois se todas as linhas da tabela receberem o mesmo valor não existe falsa violação.

O segundo update é igual ao update da Listagem 9, porém, desta vez ele contém a cláusula TOP 1, o que força que apenas uma linha seja atualizada, fazendo com que não seja possível ocorrer uma falsa violação.

O terceiro update contém uma condição que dá certeza de que apenas uma linha será atualizada. A coluna ID é chave primária da tabela, ou seja, o QO sabe que só pode existir uma linha com o valor 1. Isto novamente impede uma falsa violação.

Em nenhum dos updates da Listagem 11 o SQL perde tempo gerando o Split e etc. Ele simplesmente gera um “Narrow Update Plan” que atualiza o índice cluster e o nonclustered.

Conclusão

Analisar os planos de execução é uma tarefa muito interessante e que desperta bastante interesse na maioria dos usuários de SQL.

Existem vários itens que sempre devem ser levados em consideração na construção de um comando SQL, com destaque para as notas deste artigo. Todas estas informações ajudam na decisão de qual comando devemos utilizar e qual será a estrutura das tabelas do banco de dados.

Vimos neste artigo mais um pouco de como o QO trabalha. Também analisamos como UPDATES são executados pelo banco, e como eles são influenciados pela presença de índices nas tabelas.

Os três links apresentados como referência são essenciais para quem quer se aprofundar ainda mais no assunto. Deixo uma questão. Fora os Hints, o que mais pode influenciar nas ações do Query Optimizer?

Seletividade: Podemos definir seletividade como o grau de um valor dentro de um determinado grupo, por exemplo, os jogadores da seleção de futebol são selecionados dentre milhares de opções que um técnico tem. Aplicando este conceito a banco de dados teríamos, em uma tabela de Clientes a coluna Tipo de Pessoa, pode ser J(Jurídica) ou F(Física), podemos dizer que o grau de seletividade é baixo, pois só há duas variantes do valor dentre um todo. Por outro lado, uma coluna CPF ou CNPJ será altamente seletiva, pois irá retornar apenas um registro dentre toda a tabela.

Densidade: O termo densidade vem da física, e é calculado pela divisão entre massa e o volume; uma representação matemática seria como na Figura 6. Por exemplo, estamos um pouco mais acostumados a ouvir o termo densidade geográfica. A densidade geográfica do Brasil é calculada pela quantidade de habitantes dividida pelo tamanho da área geográfica, o que seria 187.000.000 dividido por 8.514.215,3 km2 que é igual a 21,96 habitantes por km2.

No SQL Server podemos traduzir isso para: Quanto mais densa for uma coluna, mais registros ela retorna. Repare que é exatamente o contrário de seletividade, que quanto mais seletiva for uma coluna menos registros ela retorna.

Para calcular a densidade de uma coluna, o SQL efetua a query da Listagem 12.

clip_image020

Figura 6. Calculo matemático de densidade.

SELECT (1.0 / Count(Distinct <Column>))

Listagem 12. Calculo de densidade de uma coluna.

SARG(Search Arguments): São as condições, ou filtros que você aplica em sua consulta; o valor utilizado na cláusula where. O QO usa esta informação para estimar quantas linhas serão retornadas, e qual plano utilizar para acessar os dados. Com base no argumento de consulta (SARG) o SQL consegue identificar uma série de informações que serão de extrema importância para criação do plano de consulta.

Cardinalidade: Cardinalidade é utilizada para medir o número de linhas que satisfaçam uma condição. Por exemplo, imagine uma tabela com 500 linhas e uma consulta com a clausula “where Nome = ‘Joao’”, o SQL acessa as estatísticas da coluna “Nome” e lê no histograma que o valor ‘Joao’ representa 5% da tabela, portanto a cardinalidade é de (5% * 500) = 25.

Nos planos de execução podemos considerar a cardinalidade como o “estimated number of rows” que é apresentado nas dicas dos operadores. Não preciso dizer que uma má interpretação na cardinalidade pode gerar um plano ineficiente, certo?

sys.dm_db_index_operational_stats: Esta view do sistema retorna várias informações em relação à vida de um objeto no banco de dados. Você pode utilizar esta view para identificar se seu índice está sendo bem utilizado, podendo identificar uma possível contenção em uma determinada área do índice. Consulte o help do SQL Server para ver os detalhes de cada coluna.

Links

Query processing for SQL updates

http://portal.acm.org/citation.cfm?id=1007665

Maintaining Unique Indexes

http://blogs.msdn.com/craigfr/archive/2007/09/06/maintaining-unique-indexes.aspx

Non updating updates

http://blogs.msdn.com/queryoptteam/archive/2006/07/07/659453.aspx

Categorias:Não categorizado
  1. Edilson
    11 de setembro de 2009 às 11:54

    Ae Fabiano !Espetacular esses post e o anterior !Parabens mais uma vez por essa matéria de grande valia.SugestãoGostaria de ver um post sobre como criar um baseline.Abs,Edilson

  2. Fabiano Neves
    12 de setembro de 2009 às 22:41

    Edilson, obrigado pelo elogio.Ok, vou colocar a criação do BaseLine na minha lista de assuntos para um post ok?Abraço

  3. Edilson
    15 de setembro de 2009 às 8:55

    Blzz Fabiano, aguardo ansiosamente por esse artigo !Abs

  4. fabio
    1 de dezembro de 2009 às 18:02

    Estou tntando migrar para o 2005, mas o sistema ficou extrememente lento. alguém pode me dar uma ajuda.

  5. Fabiano Neves
    2 de dezembro de 2009 às 9:19

    Olá Fábio, você já atualizou as estatisticas do banco de dados?

  6. Elisangela
    9 de abril de 2010 às 14:17

    FabianoTenho uma dúvida:Estava com problema de uma consulta no banco de dados, dropei todas as statísticas da tabela sysindex do meu banco de dados utilizando o seguinte condição para dropar os indices:where name like \’_wa_sys%\’ and not object_name (id) like \’sys%\’E deixei a opção: "auto create statistics" como false.Quais os problemas que essas configurações podem acarretar no banco de dados? Se eu utilizar o execution plan com a opção "auto create statistics" como false vou ter um resultado falso?

  1. No trackbacks yet.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: