¿Puedo hacer que SSMS me muestre los costos de la consulta real en el panel del plan de ejecución?

8

Estoy solucionando problemas de rendimiento en un procedimiento almacenado de varias declaraciones en SQL Server. Quiero saber en qué parte (s) debo pasar el tiempo.

Comprendo ¿Cómo leo el costo de la consulta? ¿Es siempre un porcentaje? que incluso cuando se le dice al SSMS que incluya un plan de ejecución real , las cifras del "costo de la consulta (en relación con el lote)" todavía se basan en estimaciones de costos , que pueden estar muy lejos de los reales

Por Medición del rendimiento de la consulta: "Costo de la consulta del plan de ejecución" frente a "Tiempo de espera" , puedo rodear la invocación del procedimiento almacenado con SET STATISTICS TIMEdeclaraciones, y luego obtendré una lista como esta en el Messagespanel:

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.

con un mensaje de salida para cada declaración.

Puedo 'fácilmente' (aunque no convenientemente) asociar el resultado de las estadísticas de tiempo con los planes de ejecución de declaración por declaración en el panel del plan de ejecución, contándolos: el cuarto SQL Server Execution Timesmensaje de salida corresponde al Query 4panel del plan de ejecución, y así sucesivamente.

Pero hay una manera mejor?

AakashM
fuente

Respuestas:

8

No conozco una manera de hacer esto en el plan de Management Studio, pero esta es una de las muchas cosas que el SentryOne Plan Explorer gratuito hará por usted cuando genere un plan real desde la herramienta: incluye todas las métricas de tiempo de ejecución por declaración.

Aaron Bertrand
fuente
Wow, esto se ve grandioso. Solo para estar seguro, las columnas Durationy CPUresultados son reales en lugar de estimaciones, ¿sí?
AakashM
@AakashM sí, esos son datos reales.
Aaron Bertrand
5

Una buena forma de hacerlo es con Profiler. Configure una "reproducción" de su proceso problemático en un cuadro de desarrollo o prueba, es decir, una llamada de muestra al proceso con parámetros. Luego, utilizando Profiler, cree una traza utilizando la plantilla TSQL_SPs o, desde una plantilla en blanco, agregue el evento SP: StmtCompleted. Agregue las columnas Duración, Lecturas, Escrituras y CPU si aún no están disponibles. Agregue un filtro a la traza en su SPID (que debe conocer de Management Studio). También puede agregar un filtro a Duración (por ejemplo, mayor que 1000 = mayor que 1 segundo).

Puede ejecutar el rastreo en Profiler aunque haya una sobrecarga (NO haga esto en un cuadro de producción) o exportar la definición y crear un rastreo del lado del servidor. La sobrecarga de Profiler no es un gran problema en un dev dedicado o cuadro de prueba.

Ejecute el proceso y deje que se complete. También puede elegir el plan de ejecución real en este momento.

Detenga su rastreo y abra el archivo, y debería ver un desglose línea por línea de su proceso, incluidos los tiempos para cada paso. Encuentro esto más útil que el plan para identificar cuellos de botella, aunque el plan será útil al mirar las secciones relevantes para ajustar.

HTH

wBob
fuente
4

También puede usar las vistas de administración dinámica sys.dm_exec_procedure_stats y sys.dm_exec_query_stats . El primero de ellos brinda información sobre el procedimiento en su conjunto; el segundo puede usarse para dividir cada consulta en el procedimiento. A continuación se muestra un ejemplo:

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

Estadísticas de procedimiento:

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 del procedimiento:

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
fuente
Esto es útil y definitivamente lo usaré en cuadros donde no puedo instalar SQL Sentry Plan Explorer.
AakashM