Wie erhalte ich geschätzte Teilbaumkosten?

7

Wenn ich eine Abfrage habe, die einen Abfrageplan zurückgibt, zum Beispiel wie folgt:

    SELECT TOP 1000 st.TEXT
    ,cp.size_in_bytes
    ,cp.plan_handle
    ,QP.query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS QP
WHERE cp.objtype = N'Adhoc'
    AND cp.usecounts = 1

Dann kann ich auf einen Abfrageplan klicken und mit der Maus über das Symbol ganz links fahren, in dem der Tipptext die geschätzten Teilbaumkosten auflistet.

Gibt es eine Möglichkeit Estimated Subtree Cost, dies als separate Spalte für meine Abfrage herauszugeben?

Ich verstehe, dass die Zahl ohne Einheiten ist und sich auf einen bestimmten Entwickler-PC vor etwa 20 Jahren bezieht. Trotzdem könnte es mir sagen, wie lange die Abfrage dauern sollte, wenn die Statistiken nicht zu weit entfernt sind.

Ich habe mich sehr bemüht, diese Informationen bei Google zu finden, aber selbst dba.stackexchange.com ist leer.

Henrik Staun Poulsen
quelle

Antworten:

7

Ich glaube, Sie müssen einige XML-Abfragen durchführen, um die geschätzten Kosten zu erhalten.

Sehen Sie, ob dies das ist, wonach Sie suchen:

   ;WITH XMLNAMESPACES  
    (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan') 
    SELECT TOP 1000 st.text
        ,cp.size_in_bytes
        ,cp.plan_handle
        ,QP.query_plan
        ,n.value('(@StatementSubTreeCost)[1]', 'VARCHAR(128)') AS StatementSubTreeCost
    FROM sys.dm_exec_cached_plans AS cp
    CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS st
    CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS QP
    CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS qn(n)
    WHERE cp.objtype = N'Adhoc'
        AND cp.usecounts = 1
    OPTION(RECOMPILE);

Dadurch werden die Kosten für einzelne Kontoauszüge innerhalb eines Stapels zurückgegeben. Möglicherweise müssen Sie in einer Gruppierung arbeiten, wenn Sie die geschätzten Gesamtkosten für den Teilbaum für den gesamten Stapel benötigen.

GoodwinSQL
quelle
1

So habe ich die Antwort auf meine Frage verwendet.

Ich bin ein großer Fan von sp_whoisactive. Wenn Sie das nicht haben, hören Sie auf zu lesen; hier herunterladen .

Also habe ich es so eingerichtet, dass alle 10 Minuten ein Schnappschuss erstellt wird, wie folgt:

DROP TABLE dbo.HESPOmonitoring_output
DECLARE @s VARCHAR(MAX)
EXEC sp_WhoIsActive 
    @output_column_list = '[login_name][dd%][session_id][program%][sql_com%][sql_text][block%][reads][writes][physical_reads][query_plan][used_memory][tempdb%][wait%][start_time][collection_time][host%][additional%]', @get_outer_command=1, @get_additional_info=1,
    @return_schema = 1, @get_plans=1, 
    @schema = @s OUTPUT
SET @s = REPLACE(@s, '<table_name>', 'dbo.HESPOmonitoring_output')
EXEC(@s)
ALTER TABLE dbo.HESPOmonitoring_output ADD HESPOmonitoring_outputID BIGINT IDENTITY(1,1) NOT NULL
go
SET NOCOUNT ON 
DECLARE @Started DATETIME=DATEADD(DAY, 3, GETDATE())
WHILE 1 > 0 BEGIN 
    EXEC sp_WhoIsActive 
        @output_column_list = '[login_name][dd%][session_id][program%][sql_com%][sql_text][block%][reads][writes][physical_reads][query_plan][used_memory][tempdb%][wait%][start_time][collection_time][host%][additional%]', @get_outer_command=1, @get_additional_info=1,
         @get_plans=1, 
        @destination_table = 'dbo.HESPOmonitoring_output'
    WAITFOR DELAY '00:10:00'
    IF GETDATE() > @Started BREAK  
END 

Ich habe es eine Weile laufen lassen (bis zu 3 Tage). Dann konvertiere ich die gesammelten Daten wie folgt:

/* this query turns HESPOmonitoring_output in a table with one row per SQL statement */
Begin TRY
    DROP TABLE #hespo
END TRY
BEGIN CATCH
END CATCH
;WITH XMLNAMESPACES  
    (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan') 
     SELECT top 10000 H.Start_Time, H.session_id, MAX(H.program_name) AS program_name, MAX(CAST(H.sql_command AS VARCHAR(max))) AS sql_command
    , MAX(CAST(H.sql_text AS VARCHAR(max))) AS sql_text
    , MAX(H.reads) AS reads
    , MAX(H.physical_reads) AS physical_reads
    , MAX(H.writes) AS writes
    , MAX(H.collection_time) AS collection_time
    , MAX(DATEDIFF(second, start_time, collection_time)) AS RunTime
    , MAX(HESPOmonitoring_outputID) AS MaxHESPOmonitoring_outputID
    , MAX(H.blocking_session_id) AS MaxBlocking_session_id
    , Min(H.blocking_session_id) AS MinBlocking_session_id
    , count_big(*) as RowCnt 
    , MAX(TRY_CAST(n.value('(@StatementSubTreeCost)[1]', 'VARCHAR(128)') AS DECIMAL(18,3))) AS StatementSubTreeCost
    INTO #hespo
    FROM dbo.HESPOmonitoring_output H
    CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS qn(n)
    GROUP BY H.Start_Time, H.session_id

Und schließlich erhalte ich eine Liste, aus der hervorgeht, wo die geschätzten Kosten im Vergleich zur Laufzeit gering sind , ohne dass der Job blockiert wird

SELECT top 10000 
H.StatementSubTreeCost/NULLIF(H.RunTime, 0) AS Ratio, H.StatementSubTreeCost, H.RunTime, *
FROM #hespo H
WHERE H.MinBlocking_session_id IS NULL
AND H.RunTime>0
AND H.StatementSubTreeCost IS NOT NULL
ORDER BY 1 

Es ist eine sehr interessante Liste , aber es kommt mit ein bisschen Lärm. Zunächst habe ich mich dafür entschieden, schnelle kleine Jobs zu ignorieren, die weniger als 10 Minuten dauern, aber diese Grenze hängt von Ihrer Situation ab.

Jetzt ist es viel einfacher, Jobs zu finden, die mit einem schlechten Plan ausgeführt werden.
Vielen Dank für Ihre Hilfe.

Henrik Staun Poulsen
quelle