SQL Server

Como otimizar o desempenho da consulta do SQL Server

Maciej Pilecki

 

Visão geral:

  • Análise de planos de execução
  • Otimização de consultas
  • Identificação de consultas a serem ajustadas

Ao otimizar seu servidor de banco de dados, ajuste o desempenho de cada consulta. Isso é tão ou mais importante que ajustar outros aspectos de instalação do seu servidor, que afetam o desempenho, tais como as configurações de hardware e de software.

Mesmo que seu servidor de banco de dados esteja sendo executado no hardware mais poderoso disponível, seu desempenho poderá ser afetado negativamente por uma série de consultas com comportamento inadequado. Na verdade, mesmo uma só consulta inadequada, às vezes chamada de "consulta de fuga", pode levar a problemas graves de desempenho no seu banco de dados.

Por outro lado, o desempenho geral do seu banco de dados pode melhorar muito com o ajuste das consultas mais caras e executadas com maior freqüência. Neste artigo, abordo algumas das técnicas que você pode empregar para identificar e ajustar as consultas mais caras e com pior desempenho no seu servidor.

Análise de planos de execução

Ao ajustar uma consulta separada, em geral você começa verificando o plano de execução dessa consulta. O plano de execução descreve a seqüência de operações, físicas e lógicas, que o SQL ServerTM executa para fazer a consulta e gerar o conjunto de resultados desejado. O plano de execução é gerado por um componente do mecanismo de banco de dados chamado Otimizador de Consultas, durante a fase de otimização do processamento de consulta. Isso leva em conta diferentes fatores, tais como os predicados de pesquisa utilizados na consulta, as tabelas envolvidas e suas condições de associação, a lista de colunas retornadas e a presença de índices úteis que podem servir como caminhos de acesso eficazes para os dados.

Em consultas complexas, pode haver uma grande quantidade de permutações possíveis; então, o otimizador de consultas não avalia todas as possibilidades, mas tenta encontrar um plano que seja "bom o suficiente" para determinada consulta. Isso ocorre pois nem sempre é possível encontrar um plano perfeito; mesmo quando isso é possível, o custo da avaliação de todas as possibilidades de encontrar um plano perfeito pode facilmente superar qualquer ganho de desempenho. Do ponto de vista de um DBA, é importante compreender o processo e suas limitações.

Há diversas formas de recuperar um plano de execução para uma consulta:

  • O Management Studio fornece os recursos Display Actual Execution Plan e Display Estimated Execution Plan, que apresentam o plano de forma gráfica. Esses recursos oferecem a solução mais adequada para exame direto, e são a abordagem utilizada com mais freqüência para exibir e analisar planos de execução. (Neste artigo, utilizarei planos gráficos gerados desta forma para ilustrar os exemplos.)
  • Várias opções SET, tais como SHOWPLAN_XML e SHOWPLAN_ALL, retornam o plano de execução como um documento XML que descreve o plano usando um esquema especial ou como um conjunto de linhas com descrição textual de cada operação do plano de execução.
  • As classes de eventos do SQL Server Profiler, como Showplan XML, permitem reunir planos de execução de declarações coletados por um rastreamento.

Apesar de uma representação XML do plano de execução não ser o formato de leitura mais fácil, esta opção permite que você escreva procedimentos e utilitários que podem analisar seus planos de execução, buscando sinais de problemas de desempenho e planos com qualidade inferior. Uma representação baseada em XML também pode ser salva em um arquivo com a extensão .sqlplan e, depois, ser aberta no Management Studio para gerar uma representação gráfica. Estes arquivos também podem ser salvos para posterior análise, certamente eliminando a necessidade de reproduzir o plano de execução toda vez que você deseja analisá-lo. Isso é especialmente útil quando você deseja comparar seus planos para verificar como eles mudam com o tempo.

Custo estimado de execução

A primeira coisa que você precisa compreender a respeito de planos de execução é como eles são gerados. O SQL Server utiliza um otimizador de consultas baseado em custo, ou seja, ele tenta gerar um plano de execução com o menor custo estimado. A estimativa se baseia na estatística de distribuição de dados disponível para o otimizador quando ele avalia cada tabela envolvida na consulta. Se essa estatística estiver ausente ou desatualizada, o otimizador de consultas não terá informações vitais necessárias ao processo de otimização da consulta e, portanto, suas estimativas provavelmente estarão erradas. Nesses casos, o otimizador escolhe um plano aquém do ideal, superestimando ou subestimando custos de execução de diferentes planos.

Há algumas suposições falsas comuns sobre o custo estimado de execução. Em especial, as pessoas costumam pressupor que o custo estimado de execução é um bom indicador de quanto tempo a consulta leva para ser executada e que essa estimativa permite diferenciar planos bons de planos ruins. Mas isso não é verdade. Primeiro, existe documentação suficiente em relação às unidades para expressar o custo estimado e se elas têm relação direta com o tempo de execução. Segundo, como se trata de uma estimativa e ela pode estar errada, planos com custos estimados superiores às vezes podem ser bem mais eficazes em termos de CPU, E/S e tempo de execução, apesar das estimativas mais altas. Isso costuma ocorrer em consultas que envolvem variáveis de tabelas. Como não há estatística disponível para as variáveis de tabelas, o otimizador de consultas sempre pressupõe que uma variável de tabela contém apenas uma linha, mesmo quando ela contém várias linhas. Então, o otimizador de consultas escolhe um plano com base em uma estimativa imprecisa. Portanto, ao comparar os planos de execução de suas consultas, não se restrinja ao custo estimado da consulta. Em vez disso, inclua a saída das opções STATISTICS I/O e STATISTICS TIME na análise para compreender o real significado da execução de custo em termos de E/S e tempo da CPU.

Vale a pena mencionar aqui um tipo especial de plano de execução, chamado de plano paralelo. Se você estiver executando sua consulta em um servidor com mais de uma CPU e sua consulta for adequada à paralelização, um plano paralelo poderá ser escolhido (Em geral, o otimizador de consultas só considera um plano paralelo para uma consulta com custo que excede determinado limite configurável). Devido à sobrecarga no gerenciamento de vários segmentos paralelos de execução (o que significa a distribuição de trabalho em segmentos, a execução da sincronização e a coleta de resultados), o custo de execução de planos paralelos é maior e isso se reflete no custo estimado. Então, por que eles têm preferência sobre planos mais baratos e não paralelos? Graças à utilização do poder de processamento de várias CPUs, os planos paralelos tendem a gerar resultados mais rápidos do que planos padrão. De acordo com o cenário específico, inclusive variáveis como recursos disponíveis e carga simultânea de outras consultas, essa situação pode ser desejável para sua configuração. Nesse caso, controle as consultas que podem gerar planos paralelos e quantas CPUs cada uma pode utilizar. Para fazer isso, defina o grau máximo da opção de paralelismo em nível de servidor e substitua-o em nível de consulta individual por OPTION (MAXDOP n), conforme necessário.

Análise de um plano de execução

Agora vejamos uma consulta simples, seu plano de execução e algumas formas de melhorar seu desempenho. Digamos que executo esta consulta através do Management Studio com a opção Include Actual Execution Plan ativada no banco de dados de amostra Adventure Works no SQL Server 2005:

SELECT c.CustomerID, SUM(LineTotal)
FROM Sales.SalesOrderDetail od 
JOIN Sales.SalesOrderHeader oh
ON od.SalesOrderID=oh.SalesOrderID
JOIN Sales.Customer c ON oh.CustomerID=c.CustomerID
GROUP BY c.CustomerID

Como resultado, vejo o plano de execução descrito na Figura 1. Esta consulta simples calcula a quantidade total de pedidos feitos por cada cliente do Adventure Works. No plano de execução, você verifica como o mecanismo do banco de dados processa a consulta e gera o resultado. Planos de execução gráfica devem ser lidos de cima para baixo e da direita para a esquerda. Cada ícone representa uma operação lógica e física executada, e as setas mostram o fluxo de dados entre as operações. A espessura das setas representa o número de linhas transferidas entre operações. Quanto mais espessa a seta, maior é o número de linhas envolvidas. Se você colocar o ponteiro sobre um dos ícones de operador, uma Dica de ferramenta amarela (como aquele mostrado na Figura 2) exibirá detalhes dessa operação específica.

Figura 1 Amostra de plano de execução

Figura 1** Amostra de plano de execução **(Clique na imagem para aumentar a exibição)

Figura 2 Detalhes de uma operação

Figura 2** Detalhes de uma operação **(Clique na imagem para aumentar a exibição)

Ao verificar cada operador, você pode analisar a seqüência de etapas executadas:

  1. O mecanismo de banco de dados examina um índice agrupado na tabela Sales.Customer e retorna a coluna CustomerID para todas as linhas dessa tabela.
  2. Depois, ele faz um exame de índice (ou seja, um índice não-agrupado) em um dos índices da tabela Sales.SalesOrderHeader. Esse é um índice da coluna CustomerID, mas ele também inclui implicitamente a coluna SalesOrderID (a chave de cluster da tabela). Os valores dessas duas colunas são retornados pelo exame.
  3. As saídas dos dois exames são associadas na coluna CustomerID através do operador físico Merge Join. (Esta é uma das três formas físicas possíveis de execução de uma operação de associação lógica. Este é um processo rápido mas exige a classificação das duas entradas em uma coluna associada. Nesse caso, os dois exames já retornaram as linhas classificadas em CustomerID; portanto, não é necessário executar a operação de classificação adicional).
  4. Em seguida, o mecanismo de banco de dados examina o índice agrupado na tabela Sales.SalesOrderDetail, recuperando os valores de quatro colunas (SalesOrderID, OrderQty, UnitPrice e UnitPriceDiscount) de todas as linhas desta tabela. (Estimava-se que 123.317 linhas seriam retornadas por esta operação e de fato esse número foi retornado, como você pode observar nas propriedades Número estimado de linhas e Número real de linhas na Figura 2. Portanto, a estimativa foi muito precisa.)
  5. Linhas geradas pelo exame de índice agrupado são transferidas para o primeiro Compute Scalar; assim, o valor da coluna computada LineTotal pode ser calculado para cada linha, com base nas colunas OrderQty, UnitPrice e UnitPriceDiscount envolvidas na fórmula.
  6. O segundo operador Compute Scalar aplica a função ISNULL ao resultado do cálculo anterior, conforme exigido pela fórmula da coluna computada. Isso conclui o cálculo da coluna LineTotal e a retorna, junto com a coluna SalesOrderID, ao próximo operador.
  7. A saída do operador Merge Join na Etapa 3 é associada à saída do operador Compute Scalar da Etapa 6, através do operador físico Hash Match.
  8. Outro operador Hash Match é aplicado para agrupar linhas retornadas de Merge Join pelo valor de coluna CustomerID e o valor agregado SUM calculado da coluna LineTotal.
  9. O último nó, SELECT, não é um operador físico ou lógico, mas um espaço reservado que representa resultados e custos gerais da consulta.

No meu laptop, este plano de execução tinha um custo estimado de 3.31365 (conforme mostrado na Figura 3). Quando executada com STATISTICS I/O ON, a consulta relatava a execução de um total de 1.388 operações de leitura lógica nas três tabelas envolvidas. Os percentuais exibidos sob cada operador representam o custo de cada operador em relação ao custo geral estimado do plano de execução inteiro. No plano da Figura 1, observe que a maior parte do custo total do plano de execução inteiro está associado a estes três operadores: o Clustered Index Scan (Exame de Índice Agrupado) da tabela Sales.SalesOrderDetail e os dois operadores Hash Match. Mas, antes de abordar a questão da otimização, gostaria de destacar uma alteração bem simples na minha consulta, que me permitirá eliminar dois operadores.

Figura 3 Custo estimado de execução total da consulta

Figura 3** Custo estimado de execução total da consulta **

Como a única coluna que retorno da tabela Sales.Customer é CustomerID, e essa coluna também é incluída como uma chave estrangeira em Sales.SalesOrderHeaderTable, posso eliminar completamente a tabela Customer da consulta, sem alterar o significado lógico nem o resultado gerado pela nossa consulta através deste código:

SELECT oh.CustomerID, SUM(LineTotal)
FROM Sales.SalesOrderDetail od JOIN Sales.SalesOrderHeader oh
ON od.SalesOrderID=oh.SalesOrderID
GROUP BY oh.CustomerID

Isso resulta em um plano de execução diferente, que é mostrado na Figura 4.

Figura 4 O plano de execução após a eliminação da tabela Customer da consulta

Figura 4** O plano de execução após a eliminação da tabela Customer da consulta **(Clique na imagem para aumentar a exibição)

Duas operações foram totalmente eliminadas – Clustered Index Scan, na tabela Customer, e Merge Join, entre Customer e SalesOrderHeader. A associação Hash Match foi substituída por Merge Join, que é bem mais eficaz. Entretanto, para utilizar Merge Join entre as tabelas SalesOrderHeader e SalesOrderDetail, foi necessário o retorno das linhas das duas tabelas classificadas pela coluna de associação SalesOrderID. Para conseguir isso, o otimizador de consultas decidiu examinar o índice agrupado na tabela SalesOrderHeader, em vez de examinar o índice não-agrupado, que seria mais barato em termos da E/S envolvida. Este é um bom exemplo de como o otimizador de consultas funciona na prática. Como as economias de custo resultantes da mudança na forma física de executar a operação de associação foram superiores ao custo adicional de E/S gerado pelo Clustered Index Scan, o otimizador de consultas escolheu a combinação resultante de operadores, pois ela forneceu o menor custo total estimado de execução. No meu laptop, apesar do aumento no número de leituras lógicas (até 1.941), o tempo de consumo da CPU diminuiu e houve uma redução de cerca de 13% (2.89548) no custo estimado de execução desta consulta.

Digamos que eu queira aperfeiçoar ainda mais o desempenho desta consulta. Observo o Clustered Index Scan da tabela SalesOrderHeader, que se tornou o operador mais caro neste plano de execução. Como só preciso de duas colunas desta tabela para realizar a consulta, posso criar um índice não-agrupado contendo apenas essas duas colunas, substituindo o exame da tabela inteira pelo exame de um índice não-agrupado bem menor. Aparência da definição de índice:

CREATE INDEX IDX_OrderDetail_OrderID_TotalLine
ON Sales.SalesOrderDetail (SalesOrderID) INCLUDE (LineTotal)

Observe que o índice que criei inclui uma coluna computada. Isso nem sempre é possível, de acordo com a definição da coluna computada.

Após criar esse índice e executar a mesma consulta, obtenho o novo plano de execução mostrado na Figura 5.

Figura 5 Plano de execução otimizado

Figura 5** Plano de execução otimizado **(Clique na imagem para aumentar a exibição)

O exame de índice agrupado na tabela SalesOrderDetail foi substituído por um exame de índice não-agrupado, com uma redução significativa no custo de E/S. Também eliminei um dos operadores Compute Scalar, pois meu índice inclui um valor já calculado da coluna LineTotal. O custo estimado do plano de execução agora é de 2.28112 e a consulta faz 1.125 leituras lógicas quando executada.

O índice abrangente

Exercício de consulta de pedidos de clientes

Pergunta: Este é o seu exercício de consulta de pedidos de clientes: tente descobrir a definição de índice, ou seja, que colunas ele deve conter para se tornar um índice abrangente desta consulta e se a ordem das colunas na definição de índice faria diferença no desempenho.

Resposta: Eu desafiei você a descobrir o índice abrangente ideal para criar uma tabela Sales.SalesOrderHeader para a consulta de amostra do meu artigo. Para conseguir isso, a primeira coisa a ser observada é que a consulta utiliza apenas duas colunas da tabela: CustomerID e SalesOrderID. Após uma leitura cuidadosa do meu artigo, você notará que, no caso da tabela SalesOrderHeader, já existe um índice que aborda esta consulta. Ele é o índice de CustomerID e contém implicitamente a coluna SalesOrderID, que é a chave de cluster da tabela.

É claro que também expliquei por que o otimizador da consulta decidiu não utilizar este índice. Você poderia forçar o otimizador de consultas a utilizar este índice, mas a solução seria menos eficaz do que o plano existente que utiliza os operadores Clustered Index Scan e Merge Join. Isso ocorreria pois você forçaria o otimizador de consultas a optar entre executar a operação adicional Classificar (para poder continuar utilizando o Merge Join) ou voltar a utilizar um Hash Join menos eficaz. As duas opções possuem um custo estimado de execução superior ao plano existente (a versão com a operação Classificar teria desempenho fraco); então, o otimizador de consultas não as utilizará, a menos que seja forçado a fazê-lo. Então, nesse caso, o único índice que terá melhor desempenho do que o Clustered Index Scan é um índice não-agrupado em SalesOrderID, CustomerID. Mas note que as colunas precisam estar exatamente nesta ordem:

CREATE INDEX IDX_OrderHeader_SalesOrderID_CustomerID
ON Sales.SalesOrderHeader (SalesOrderID, CustomerID)

Se você criar este índice, o plano de execução conterá o Exame de índice e não o operador Clustered Index Scan (Exame de Índice Agrupado). Essa é uma diferença significativa. Nesse caso, o índice não-agrupado contendo apenas duas colunas é bem menor do que a tabela inteira no formato de um índice agrupado. Então, ele exigirá menos E/S para ler os dados necessários.

Este exemplo também mostra como a ordem das colunas no índice pode ter um impacto significativo sobre sua utilidade para o otimizador de consultas. Lembre-se disso ao criar índices com múltiplas colunas

O índice que criei em SalesOrderDetail é um exemplo do chamado "índice abrangente". Ele é um índice não-agrupado que contém todas as colunas necessárias para concluir a consulta, dispensando o exame da tabela inteira através do operador Table Scan ou do Clustered Index Scan. O índice é, em essência, uma cópia menor da tabela, contendo um subconjunto de colunas da tabela. Apenas as colunas necessárias para responder a(s) consulta(s) são incluídas no índice, ou seja, o índice contém apenas o necessário para "abranger" a consulta.

A criação de índices abrangentes para as consultas executadas com maior freqüência é uma das técnicas mais fáceis e comuns utilizadas no ajuste de consultas. Ela funciona muito bem em situações em que a tabela contém várias colunas, mas apenas algumas delas são freqüentemente referenciadas por consultas. Ao criar um ou mais índices abrangentes, você pode melhorar muito o desempenho das consultas afetadas, pois elas estarão acessando um volume de dados bem menor e, assim, incorrendo em menor E/S. Contudo, há um custo oculto de manutenção de índices adicionais durante as operações de modificação de dados (INSERT, UPDATE e DELETE). De acordo com o ambiente e a relação entre as consultas SELECT e as modificações de dados, analise com cuidado se essa sobrecarga adicional de manutenção de índice é compensada por melhorias no desempenho da consulta.

Não tenha medo de criar índices com múltiplas colunas, ou ficará restrito a índices de uma única coluna. Os índices de múltiplas colunas tendem a ser bem mais úteis do que os de uma única coluna. e a preferência do otimizador de consultas para abranger a consulta. A maioria dos índices abrangentes são índices de múltiplas colunas.

No caso da minha consulta de amostra, ainda há necessidade de aperfeiçoamentos, e essa consulta poderá ser mais otimizada se um índice abrangente for inserido na tabela SalesOrderHeader. Isso elimina o Clustered Index Scan (Exame de Índice Agrupado) para dar lugar a um Exame de índice não-agrupado. Deixarei isso como um exercício para você. Tente descobrir a definição de índice, ou seja, que colunas ele deve conter para se tornar um índice abrangente desta consulta e se a ordem das colunas na definição de índice faria diferença no desempenho. Consulte a barra lateral "Exercício de consulta de pedidos de clientes" para obter a solução.

Modos de exibição indexados

Se o desempenho de minha consulta de exemplo é muito importante, posso avançar um pouco e criar um modo de exibição indexado que armazena fisicamente os resultados materializados da consulta. Há alguns pré-requisitos e limitações em modos de exibição indexados, mas se você conseguir utilizar um deles, poderá melhorar muito o desempenho. Lembre-se de que o custo de manutenção dos modos de exibição indexados é maior do que em índices padrão. Por isso, convém utilizá-los com cautela. Nesse caso, esta é a aparência da definição de índice:

CREATE VIEW vTotalCustomerOrders
WITH SCHEMABINDING
AS
SELECT oh.CustomerID, SUM(LineTotal) AS OrdersTotalAmt, COUNT_BIG(*) AS TotalOrderLines
FROM Sales.SalesOrderDetail od 
JOIN Sales.SalesOrderHeader oh
ON od.SalesOrderID=oh.SalesOrderID
GROUP BY oh.CustomerID 

Observe a opção WITH SCHEMABINDING, um pré-requisito para criar um índice neste modo de exibição, e a função COUNT_BIG(*), necessária quando nossa definição de índice contém uma função agregada (neste exemplo, SUM). Após criar este modo de exibição, posso criar um índice nele, como:

CREATE UNIQUE CLUSTERED INDEX CIX_vTotalCustomerOrders_CustomerID 
ON vTotalCustomerOrders(CustomerID)

Quando crio este índice, o resultado da consulta incluído na definição do modo de exibição é materializado e fisicamente armazenado em disco no índice. Observe que todas as operações de modificação de dados nas tabelas-base atualizam automaticamente os valores no modo de exibição, de acordo com sua definição.

Se eu executar a consulta novamente, o resultado dependerá da edição do SQL Server em execução. Em edições do Enterprise ou Developer, o otimizador de consultas coincide automaticamente essa consulta com a definição do modo de exibição indexado, e utiliza o modo de exibição indexado em vez de consultar as tabelas-base envolvidas. A Figura 6 mostra um plano de execução gerado neste caso. Ele consiste em apenas uma operação, um Clustered Index Scan (Exame de índice agrupado) que criei no modo de exibição. O custo estimado da execução é apenas de 0,09023 e ela faz apenas 92 leituras lógicas.

Figura 6 Plano de execução ao utilizar o modo de exibição indexado

Figura 6** Plano de execução ao utilizar o modo de exibição indexado **(Clique na imagem para aumentar a exibição)

É possível criar e utilizar este modo de exibição indexado em outras edições do SQL Server. Mas, para alcançar o mesmo efeito, você precisa mudar a consulta para referenciar diretamente o modo de exibição usando a dica NOEXPAND, desta forma:

SELECT CustomerID, OrdersTotalAmt
FROM vTotalCustomerOrders WITH (NOEXPAND)

Como pode ver, modos de exibição indexados podem ser um recurso poderoso quando utilizados de forma adequada. Eles são mais úteis para otimizar consultas que fazem agregações em grandes volumes de dados. Quando são utilizados na edição Enterprise, eles podem beneficiar várias consultas, sem exigir mudanças no seu código.

Identificação de consultas a serem ajustadas

Como posso identificar as consultas que precisam ser ajustadas? Busco as consultas executadas com maior freqüência. Talvez o custo de uma única execução não seja muito alto, mas o custo agregado da execução pode ser bem maior do que o de uma grande consulta que raramente é executada. Isso não significa que você não deva ajustar consultas grandes. Mas acredito que é melhor se concentrar primeiro nas consultas executadas com maior freqüência. Mas como identificá-las?

Infelizmente, o método mais confiável é um pouco complexo e envolve a execução de um rastreamento de todas as consultas executadas no seu servidor e, depois, o seu agrupamento por assinaturas. (Ou seja, o texto da consulta com os valores de parâmetro reais substituídos por espaços reservados para identificar o mesmo tipo de consulta, mesmo quando ela é executada com valores de parâmetro diferentes.) Esse processo é complexo, pois é difícil gerar assinaturas de consulta. Itzik Ben-Gan descreve uma solução utilizando funções CLR definidas pelo usuário e expressões comuns no seu livro Inside Microsoft SQL Server 2005: T-SQL Querying.

Há outro método mais simples, embora um pouco menos confiável. Você pode se basear em estatísticas mantidas para todas as consultas no cache do plano de execução e consultá-las através de modos de exibição de gerenciamento dinâmico. A Figura 7 contém uma consulta de amostra que exibe o texto e o plano de execução das 20 consultas no seu cache, com o maior número acumulado de leituras lógicas. Essa consulta é muito útil para identificar rapidamente as consultas que estão gerando o maior número de leituras lógicas, mas ela tem limitações. Ou seja, ela só mostrará as consultas com planos em cache no momento de execução da consulta. Você não terá acesso ao que estiver sem cache.

Figure 7 Identificação das 20 consultas mais caras em termos de E/S de leitura

SELECT TOP 20 SUBSTRING(qt.text, (qs.statement_start_offset/2)+1, 
        ((CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(qt.text)
         ELSE qs.statement_end_offset
         END - qs.statement_start_offset)/2)+1), 
qs.execution_count, 
qs.total_logical_reads, qs.last_logical_reads,
qs.min_logical_reads, qs.max_logical_reads,
qs.total_elapsed_time, qs.last_elapsed_time,
qs.min_elapsed_time, qs.max_elapsed_time,
qs.last_execution_time,
qp.query_plan
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) qt
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE qt.encrypted=0
ORDER BY qs.total_logical_reads DESC

Após identificar as consultas com desempenho insatisfatório, observe seus planos de consulta e busque formas de melhorar seu desempenho, utilizando algumas das técnicas de indexação descritas anteriormente neste artigo. Se for bem-sucedido, você notará que investiu bem seu tempo.

Bom ajuste!

Maciej Pilecki é um Mentor Associado da Solid Quality Mentors, organização global especializada em treinamento, aconselhamento profissional e consultoria. Ele é um MCT (Microsoft Certified Trainer) e um MVP (Most Valuable Professional) do SQL Server, ministra cursos e conferências sobre vários aspectos do SQL Server e do desenvolvimento de aplicativo.

© 2008 Microsoft Corporation e CMP Media, LLC. Todos os direitos reservados. A reprodução parcial ou completa sem autorização é proibida..