Puis-je demander à SSMS de me montrer les coûts réels des requêtes dans le volet Plan d'exécution?

8

Je corrige des problèmes de performances sur une procédure stockée à plusieurs états dans SQL Server. Je veux savoir sur quelle (s) partie (s) je dois passer du temps.

Je comprends de Comment lire le coût de la requête, et est-ce toujours un pourcentage? que même lorsque SSMS est invité à inclure le plan d'exécution réel , les chiffres du "coût de la requête (par rapport au lot)" sont toujours basés sur des estimations de coûts , qui peuvent être très éloignées des chiffres réels

D'après la mesure des performances des requêtes: «Execution Plan Query Cost» vs «Time Taken», je comprends que je peux entourer l'invocation de la procédure stockée d' SET STATISTICS TIMEinstructions, et j'obtiendrai ensuite une liste comme celle-ci dans le Messagesvolet:

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.

avec un message de sortie pour chaque instruction.

Je peux `` facilement '' (mais pas commodément) associer la sortie des statistiques de temps aux plans d'exécution instruction par instruction dans le volet Plan d'exécution, en les comptant: la quatrième SQL Server Execution Timessortie de message correspond à Query 4dans le volet Plan d'exécution, etc.

Mais y a-t-il une meilleure façon?

AakashM
la source

Réponses:

8

Je ne connais pas de moyen de le faire dans le plan de Management Studio, mais c'est l'une des nombreuses choses que l' explorateur de plans gratuit SentryOne fera pour vous lorsque vous générez un plan réel à partir de l'outil - il comprend tous les métriques d'exécution par instruction.

Aaron Bertrand
la source
Wow, ça a l'air grand. Juste pour être sûr, les colonnes Durationet les CPUrésultats sont des chiffres réels plutôt que des estimations, oui?
AakashM
@AakashM oui, ce sont des chiffres réels.
Aaron Bertrand
5

Une bonne façon de le faire est avec Profiler. Configurez une "repro" de votre problème sur un dev ou une boîte de test, c'est-à-dire un exemple d'appel au proc avec des paramètres. Ensuite, à l'aide de Profiler, créez une trace à l'aide du modèle TSQL_SPs ou à partir d'un modèle vierge, ajoutez l'événement SP: StmtCompleted. Ajoutez les colonnes Durée, Lectures, Écritures et CPU si elles ne sont pas déjà disponibles. Ajoutez un filtre à la trace sur votre SPID (que vous devez connaître dans Management Studio). Vous pouvez également ajouter un filtre à la durée (par exemple supérieur à 1000 = supérieur à 1 seconde).

Vous pouvez soit exécuter la trace dans le Générateur de profils bien qu'il y ait des frais généraux (ne PAS faire cela sur une boîte de production) ou exporter la définition et créer une trace côté serveur. La surcharge du profileur n'est pas vraiment un problème sur une boîte de développement ou de test dédiée.

Exécutez le proc et laissez-le terminer. Vous pouvez également choisir de collecter le plan d'exécution réel à ce stade.

Arrêtez votre trace et ouvrez le fichier, et vous devriez voir une ventilation ligne par ligne de votre proc, y compris les horaires pour chaque étape. Je trouve cela plus utile que le plan pour identifier les goulots d'étranglement, bien que le plan vous soit utile lorsque vous examinerez les sections pertinentes à régler.

HTH

wBob
la source
4

Vous pouvez également utiliser les vues de gestion dynamique sys.dm_exec_procedure_stats et sys.dm_exec_query_stats . La première donne des informations sur la procédure dans son ensemble; la seconde peut être utilisée pour décomposer chaque requête de la procédure. Un exemple est illustré ci-dessous:

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%';

Statistiques de procédure:

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');

Requêtes dans la procédure:

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
la source
Ceci est utile et je vais certainement l'utiliser sur des boîtes où je ne peux pas installer SQL Sentry Plan Explorer.
AakashM