web-dev-qa-db-fra.com

Comment obtenir des coûts de sous-groupe estimés?

Si j'ai une requête qui renvoie un requérant_plan, par exemple comme celui-ci:

    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

Ensuite, je peux cliquer sur un query_plan et survoler sur la plupart des icônes de gauche, où le texte de la pointe énumère le coût de la sous-arbitre estimé.

Y a-t-il un moyen d'obtenir ça Estimated Subtree Cost Out comme une colonne séparée pour ma requête?

Je comprends que le nombre est moins unitaire et fait référence à un PC de développeurs particuliers il y a environ 20 ans. Même donc, je pense que cela pourrait me dire combien de temps la requête devrait prendre si des statistiques ne sont pas trop éloignées.

J'ai essayé très fort pour Google pour cette information, mais même dba.stackexchange.com se présente vide.

7

Je crois que vous devrez faire du travail de requête XML pour obtenir ce coût estimé.

Voir si c'est ce que vous recherchez:

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

Cela reviendra le coût des déclarations individuelles à l'intérieur d'un lot. Vous devrez peut-être travailler dans un groupe de regroupement si vous avez besoin du coût total de la sous-arbitre estimé pour l'ensemble du lot.

7
GoodwinSQL

Voici comment j'ai utilisé la réponse à ma question.

Je suis un grand fan de sp_whoisactive. Si vous n'avez pas cela, arrêtez de lire; Téléchargez ici .

Je l'ai donc mis en place pour recueillir un coup de pression toutes les 10 minutes, comme celui-ci:

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 

Je l'ai laissé courir pendant un moment (jusqu'à 3 jours). Ensuite, je convertis les données collectées comme ceci:

/* 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

Et enfin, je reçois une liste qui me montre où le coût estimé est petit par rapport au runtime, sans que le travail soit bloqué du tout

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 

C'est une liste très intéressante , mais elle vient avec un peu de bruit. Pour un début, j'ai choisi d'ignorer rapidement les petits travaux de moins de 10 minutes, mais cette limite dépend de votre situation.

Maintenant, il est beaucoup plus facile de trouver des emplois qui gèrent avec un mauvais plan.
Merci beaucoup pour votre aide.

1