Posso obter o SSMS para me mostrar os custos reais da consulta no painel do plano de execução?

8

Estou corrigindo problemas de desempenho em um procedimento armazenado com várias instruções no SQL Server. Quero saber em quais partes devo gastar tempo.

Entendo em Como leio o Custo de consulta e é sempre uma porcentagem? que mesmo quando o SSMS é instruído a incluir o plano de execução real , os números "Custo da consulta (relativo ao lote)" ainda são baseados em estimativas de custo , que podem estar longe dos valores reais

Entendo, Medindo o desempenho da consulta: "Custo da consulta do plano de execução" vs "Tempo decorrido", que posso cercar a invocação do procedimento armazenado com SET STATISTICS TIMEinstruções e, em seguida, receberei uma lista como esta no Messagespainel:

SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 0 ms.

[etc]

 SQL Server Execution Times:
   CPU time = 187 ms,  elapsed time = 206 ms.

com uma mensagem de saída para cada instrução.

Posso 'facilmente' (embora não seja conveniente) associar a saída de estatísticas de tempo aos planos de execução de declaração por instrução no painel Plano de execução, contando-os: A quarta SQL Server Execution Timessaída de mensagem corresponde a Query 4no painel Plano de execução e assim por diante.

Mas existe uma maneira melhor?

AakashM
fonte

Respostas:

8

Não conheço uma maneira de fazer isso no plano do Management Studio, mas essa é uma das muitas coisas que o SentryOne Plan Explorer gratuito fará por você quando você gerar um plano real a partir da ferramenta - ele inclui todos os métricas de tempo de execução por instrução.

Aaron Bertrand
fonte
Uau, isso parece ótimo. Só para ter certeza, as colunas Duratione CPUresultados são reais e não estimativas, sim?
AakashM
@AakashM sim, esses são dados reais.
Aaron Bertrand
5

Uma boa maneira de fazer isso é com o Profiler. Configure uma "reprodução" do seu proc do problema em uma caixa de desenvolvimento ou teste, ou seja, uma chamada de amostra para o proc com parâmetros. Em seguida, usando o Profiler, crie um rastreamento usando o modelo TSQL_SPs ou, a partir de um modelo em branco, adicione o evento SP: StmtCompleted. Adicione as colunas Duração, Leitura, Gravação e CPU, se ainda não estiverem disponíveis. Adicione um filtro ao rastreamento no seu SPID (que você deve conhecer no Management Studio). Você também pode adicionar um filtro à Duração (por exemplo, maior que 1000 = maior que 1 segundo).

Você pode executar o rastreamento no Profiler, embora haja sobrecarga (NÃO faça isso em uma caixa de produção) ou exportar a definição e criar um rastreamento no lado do servidor. A sobrecarga do Profiler não é muito importante em um desenvolvedor ou caixa de teste dedicada.

Execute o proc e deixe-o concluir. Você também pode coletar o plano de Execução Real neste momento.

Interrompa seu rastreio e abra o arquivo, e você verá uma linha por linha do seu proc, incluindo horários para cada etapa. Acho isso mais útil do que o plano para identificar gargalos, embora o plano seja útil ao examinar as seções relevantes a serem ajustadas.

HTH

wBob
fonte
4

Você também pode usar os sys.dm_exec_procedure_stats e sys.dm_exec_query_stats exibições de gerenciamento dinâmico. O primeiro deles fornece informações sobre o procedimento como um todo; o segundo pode ser usado para interromper cada consulta no procedimento. Um exemplo é mostrado abaixo:

USE AdventureWorks;
GO
CREATE PROCEDURE dbo.Test
    @NameLike nvarchar(50)
AS
BEGIN
    SELECT
        ProductCount = COUNT_BIG(*)
    FROM Production.Product AS p
    JOIN Production.TransactionHistory AS th ON
        th.ProductID = p.ProductID
    WHERE
        p.Name LIKE @NameLike;

    SELECT
        pc.Name,
        ProductCount = COUNT_BIG(*)
    FROM Production.Product AS p
    JOIN Production.ProductSubcategory AS ps ON
        ps.ProductSubcategoryID = p.ProductSubcategoryID
    JOIN Production.ProductCategory AS pc ON
        pc.ProductCategoryID = ps.ProductCategoryID
    WHERE
        p.Name LIKE @NameLike
    GROUP BY
        pc.Name
    ORDER BY
        pc.Name;
END;
GO
EXECUTE dbo.Test @NameLike = N'A%';
EXECUTE dbo.Test @NameLike = N'F%';

Estatísticas do procedimento:

SELECT
    deps.last_execution_time,
    deps.last_worker_time,
    deps.last_physical_reads,
    deps.last_logical_writes,
    deps.last_logical_reads,
    deps.last_elapsed_time
FROM sys.dm_exec_procedure_stats AS deps
WHERE
    deps.database_id = DB_ID()
    AND deps.[object_id] = OBJECT_ID(N'dbo.Test', N'P');

Consultas dentro do procedimento:

SELECT
    query.the_text,
    deqs.last_execution_time,
    deqs.last_worker_time,
    deqs.last_physical_reads,
    deqs.last_logical_writes,
    deqs.last_logical_reads,
    deqs.last_clr_time,
    deqs.last_elapsed_time,
    deqs.last_rows    -- note: Only present from 2008 R2 onwards
FROM sys.dm_exec_query_stats AS deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.[sql_handle]) AS dest
CROSS APPLY
(
    VALUES 
    (
        SUBSTRING
        (
            dest.[text], 
            deqs.statement_start_offset / 2 + 1,
            (ISNULL(NULLIF(deqs.statement_end_offset, -1), DATALENGTH(dest.[text])) - deqs.statement_start_offset) / 2 + 1
        )
    )
) AS query (the_text)
WHERE
    deqs.[sql_handle] IN
    (
        SELECT
            deps.[sql_handle]
        FROM sys.dm_exec_procedure_stats AS deps
        WHERE
            deps.database_id = DB_ID()
            AND deps.[object_id] = OBJECT_ID(N'dbo.Test', N'P')
    );
Paul White 9
fonte
Isso é útil e definitivamente vou usá-lo em caixas em que não consigo instalar o SQL Sentry Plan Explorer.
AakashM