Gespeicherte Prozeduren mit fehlenden Indizes finden

7

So suchen Sie nach gespeicherten Prozessen, für die Indizes erstellt werden müssen.

Hat jemand eine Idee? Grundsätzlich haben wir Prozeduren gespeichert, die sehr langsam ablaufen. Ich frage mich, ob es eine Art automatisiertes Skript oder eine App gibt, die scannen kann, um anzuzeigen, ob ein Index erstellt werden muss.

Vsjfkvkkc
quelle
1
Welche Version von SQL Server verwenden Sie?
Aaron Bertrand

Antworten:

7

Nachdem Sie nicht viel darüber nachgedacht und nur die spezifische Frage beantwortet haben, können Sie hier alle gespeicherten Prozeduren identifizieren, die auf Tabellen verweisen, die zu einem bestimmten Zeitpunkt als Kandidaten für zusätzliche Indizes identifiziert wurden. Leider ist dies eine sehr lockere Beziehung ... Ich glaube nicht, dass es eine einfache Möglichkeit gibt zu sagen, dass "diese gespeicherte Prozedur diese Tabelle abgefragt hat und diesen vorgeschlagenen Index hätte verwenden können", es sei denn, Sie haben begonnen, den SQL-Text zu analysieren und den Fuzzy-Abgleich mit den Spalten durchzuführen usw. empfohlen durch die fehlenden Index-DMVs. Dies ist ein Anfangin Richtung dieses Prozesses. Trotzdem stimme ich Henry zu, es gibt viele direktere Möglichkeiten, um Leistungsprobleme in von Ihnen identifizierten gespeicherten Prozeduren zu analysieren und anzugehen. Die Prüfung des Ausführungsplans sollte beispielsweise einige Hinweise viel intuitiver liefern als diese Rundum-Methode, die Sie verfolgen möchten. In jedem Fall ist hier eine Abfrage, um Sie zu starten:

SELECT 
  [Procedure] = QUOTENAME(OBJECT_SCHEMA_NAME(s.[object_id])) 
        + '.' + QUOTENAME(OBJECT_NAME(s.[object_id])), 
  [Table]     = QUOTENAME(r.referenced_schema_name) 
        + '.' + QUOTENAME(r.referenced_entity_name),
  i.equality_columns, 
  i.inequality_columns,
  i.included_columns,
  s.execution_count,
  avg_time = s.total_elapsed_time*1.0/s.execution_count
FROM sys.dm_exec_procedure_stats AS s
CROSS APPLY sys.dm_sql_referenced_entities
(
  QUOTENAME(OBJECT_SCHEMA_NAME(s.[object_id]))
  + '.' + QUOTENAME(OBJECT_NAME(s.[object_id])), 'OBJECT'
) AS r
INNER JOIN sys.dm_db_missing_index_details AS i
ON i.[object_id] = r.referenced_id
WHERE r.referenced_minor_id = 0;

Hinweis: Die DMVs haben normalerweise nur Daten seit dem letzten Neustart.

Aaron Bertrand
quelle
4

Ab SS 2005 gibt es eine DMV mit dem Namen sys.dm_db_missing_index_details. Google das und Sie werden einige nützliche Skripte finden.

Das heißt, Sie müssen das Problem möglicherweise ganzheitlicher betrachten - ist Ihre Hardware ausreichend (Perfmon-Zähler helfen Ihnen dabei), was läuft zur gleichen Zeit noch, worauf wartet SQL hauptsächlich (Google die DMV sys.dm_os_wait_stats) für einige großartige Skripte). Sie können sich den Ausführungsplan der jeweiligen gespeicherten Prozedur ansehen, um herauszufinden, wo der Engpass liegt.

Henry Lee
quelle
0

Erweiterung des Beitrags von @Aaron Bertrand durch Hinzufügen einer allgemeinen Optimierung des Abfrageindex (und automatische Generierung der DDL für die neuen Indikatoren) gemäß @ Rorys Antwort hier: https://stackoverflow.com/a/26060183/4228193 :

sp_updatestats
go
SELECT 
  CONVERT (decimal (28,1), (
    migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) + 
    migs.avg_total_system_cost * migs.avg_system_impact * (migs.system_seeks + migs.system_scans) +
    COALESCE(SPs.total_elapsed_time*1.0/ 
      290/ --based on 'SELECT total_elapsed_time/(DATEDIFF(second, start_time, GETDATE())+1.0) FROM sys.dm_exec_requests' for my personal install of SQL Server
      1000/ --otherwise seems closer to ms; total seconds of execution to get here
      425 -- the approximate relationship between avg_total_user_cost and the average of total_elapsed_time for a stored procedure on my system
    , 0) * (migs.avg_user_impact + migs.avg_system_impact)
  )) AS improvement_measure,
  COALESCE(QUOTENAME(DB_NAME(SPs.database_id))+'.','') +
    COALESCE(QUOTENAME(OBJECT_SCHEMA_NAME(SPs.[object_id]))+'.','') +
    COALESCE(QUOTENAME(OBJECT_NAME(SPs.[object_id])),CAST(SPs.[object_id] as nvarchar(128)),'N/A') as [ProcedureAppliesTo], 
  '  CREATE INDEX
       [missing_index_' + CONVERT(varchar, mig.index_group_handle) + '_' + CONVERT(varchar, mid.index_handle) + ']
     ON ' + mid.statement + '
       (' + ISNULL (mid.equality_columns,'') +
            CASE WHEN mid.equality_columns IS NOT NULL
                  AND mid.inequality_columns IS NOT NULL 
              THEN ','
              ELSE ''
            END +
            ISNULL(mid.inequality_columns, '') + '
       )' + 
     ISNULL(' INCLUDE (' + mid.included_columns + ')', '') as [create_index_statement],
  COALESCE(QUOTENAME(SPs.referenced_schema_name)+'.',QUOTENAME(OBJECT_SCHEMA_NAME(mid.object_id))+'.','') +
    COALESCE(QUOTENAME(SPs.referenced_entity_name),QUOTENAME(OBJECT_NAME(mid.object_id)),'') as [TableName],
  SPs.exec_count,
  SPs.total_elapsed_time*1.0/ 
    290/ --based onselect total_elapsed_time/(DATEDIFF(second, start_time, GETDATE())+1.0) from sys.dm_exec_requests 
    1000/ --otherwise seems closer to ms
    SPs.exec_count as [avg_time_sp],
  SPs.total_elapsed_time*1.0/ 
    290/ --based onselect total_elapsed_time/(DATEDIFF(second, start_time, GETDATE())+1.0) from sys.dm_exec_requests 
    1000/ --otherwise seems closer to ms
    SPs.exec_count/
    425 as avg_total_user_cost_sp,
  SPs.cacheobjtype,
  SPs.objtype,
  CASE WHEN migs.last_user_seek>migs.last_system_seek THEN migs.last_user_seek ELSE COALESCE(migs.last_system_seek,migs.last_user_seek) END as last_seek,
  CASE WHEN migs.last_user_scan>migs.last_system_scan THEN migs.last_user_scan ELSE COALESCE(migs.last_system_scan,migs.last_user_scan) END as last_scan,
  SPs.last_execution_timestamp,
  CONVERT(varchar, getdate(), 121) AS this_query_time_of_exec
FROM
  sys.dm_db_missing_index_details AS mid
INNER JOIN sys.dm_db_missing_index_groups AS mig
  ON mig.index_handle = mid.index_handle
INNER JOIN sys.dm_db_missing_index_group_stats as migs
  ON migs.group_handle = mig.index_group_handle
LEFT JOIN (
  SELECT
    CASE WHEN SUM(eps.execution_count)>=SUM(cp.usecounts) THEN SUM(eps.execution_count) ELSE COALESCE(SUM(cp.usecounts),SUM(eps.execution_count)) END as exec_count,
    SUM(eps.total_elapsed_time) as total_elapsed_time,
    MAX(cp.cacheobjtype) as cacheobjtype,
    MAX(cp.objtype) as objtype,
    MAX(eps.last_execution_time) as last_execution_timestamp,
    re.referenced_entity_name,
    re.referenced_schema_name,
    re.referenced_id,
    eps.database_id,
    eps.[object_id]
  FROM
    sys.dm_exec_procedure_stats AS eps
  INNER JOIN sys.dm_exec_cached_plans as cp
    ON cp.plan_handle=eps.plan_handle
  CROSS APPLY sys.dm_sql_referenced_entities (
    COALESCE(QUOTENAME(OBJECT_SCHEMA_NAME(eps.[object_id]))+'.','') +
      COALESCE(QUOTENAME(OBJECT_NAME(eps.[object_id])),CAST(eps.[object_id] as nvarchar(128)),''),
    'OBJECT'
  ) AS re
  GROUP BY
    re.referenced_entity_name,
    re.referenced_schema_name,
    re.referenced_id,
    eps.database_id,
    eps.[object_id]
) as SPs
  ON mid.[object_id] = SPs.referenced_id

Abhängig von den Besonderheiten Ihrer SQL-Installation müssen Sie möglicherweise die "Konstanten" 290und anpassen 425, die 2x bzw. 3x erscheinen.

mpag
quelle