Kann ich SSMS veranlassen, mir die tatsächlichen Abfragekosten im Bereich Ausführungsplan anzuzeigen?

8

Ich behebe Leistungsprobleme bei einer in SQL Server gespeicherten Prozedur mit mehreren Anweisungen. Ich möchte wissen, für welche Teile ich Zeit verbringen soll.

Ich verstehe aus Wie lese ich Abfragekosten und ist es immer ein Prozentsatz? dass , selbst wenn SSMS erzählt tatsächlichen Ausführungsplan enthalten , nach wie vor die „Abfragekosten (bezogen auf die Charge)“ Zahlen auf Kosten basiert Schätzungen , die aus Wirklichkeiten sein weit kann

Ich verstehe aus der Messung der Abfrageleistung: "Abfragekosten für Ausführungsplan" im Vergleich zu "Zeitaufwand", dass ich den Aufruf der gespeicherten Prozedur mit SET STATISTICS TIMEAnweisungen umgeben kann, und erhalte dann eine Liste wie diese im Bereich Messages:

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.

mit einer Ausgangsnachricht für jede Anweisung.

Ich kann die Zeitstatistikausgabe 'einfach' (wenn auch nicht bequem) mit den Ausführungsplänen für Anweisungen für Anweisungen im Ausführungsplanbereich verknüpfen, indem ich sie zähle: Die vierte SQL Server Execution TimesNachrichtenausgabe entspricht Query 4im Ausführungsplanbereich usw.

Aber gibt es einen besseren Weg?

AakashM
quelle

Antworten:

8

Ich kenne keine Möglichkeit, dies im Plan von Management Studio zu tun, aber dies ist eines der vielen Dinge, die der kostenlose SQL Sentry Plan Explorer für Sie erledigt, wenn Sie einen tatsächlichen Plan aus dem Tool heraus generieren - er enthält alles die Laufzeitmetriken pro Anweisung.

http://sqlsentry.net/plan-explorer/sql-server-query-view.asp

Haftungsausschluss: Ich arbeite für SQL Sentry.

Aaron Bertrand
quelle
Wow, das sieht großartig aus. Nur damit ich sicher bin, sind die Spalten Durationund die CPUErgebnisse eher Istwerte als Schätzungen, ja?
AakashM
@AakashM ja, das sind Istwerte.
Aaron Bertrand
5

Ein guter Weg, dies zu tun, ist mit Profiler. Richten Sie einen "Repro" Ihres Problemprozesses auf einem Entwickler oder einer Testbox ein, dh einen Beispielaufruf an den Prozess mit Parametern. Erstellen Sie dann mit Profiler einen Trace mithilfe der Vorlage TSQL_SPs oder fügen Sie aus einer leeren Vorlage das Ereignis SP: StmtCompleted hinzu. Fügen Sie die Spalten Duration, Reads, Writes und CPU hinzu, falls diese noch nicht verfügbar sind. Fügen Sie dem Trace in Ihrer SPID einen Filter hinzu (den Sie aus Management Studio kennen sollten). Sie können der Dauer auch einen Filter hinzufügen (z. B. größer als 1000 = größer als 1 Sekunde).

Sie können den Trace entweder in Profiler ausführen, obwohl Overhead besteht (tun Sie dies NICHT in einer Produktionsbox) oder die Definition exportieren und einen serverseitigen Trace erstellen. Der Profiler-Overhead ist bei einem dedizierten Entwickler oder einer Testbox nicht so wichtig.

Führen Sie den Prozess aus und lassen Sie ihn abschließen. Sie können auch den tatsächlichen Ausführungsplan an dieser Stelle abholen.

Stoppen Sie Ihre Ablaufverfolgung und öffnen Sie die Datei. Sie sollten eine zeilenweise Aufschlüsselung Ihres Prozesses sehen, einschließlich der Zeitangaben für jeden Schritt. Ich finde dies nützlicher als den Plan zur Identifizierung von Engpässen, obwohl der Plan nützlich sein wird, wenn man sich die relevanten Abschnitte ansieht, um sie zu optimieren.

HTH

wBob
quelle
4

Sie können auch die dynamischen Verwaltungsansichten sys.dm_exec_procedure_stats und sys.dm_exec_query_stats verwenden . Die erste gibt Auskunft über das gesamte Verfahren; Die zweite kann verwendet werden, um jede Abfrage in der Prozedur aufzuteilen. Ein Beispiel ist unten gezeigt:

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

Verfahrensstatistik:

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

Abfragen innerhalb der Prozedur:

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
quelle
Dies ist nützlich und ich werde dies definitiv für Boxen verwenden, in denen ich SQL Sentry Plan Explorer nicht installieren kann.
AakashM