Identification des procédures stockées inutilisées

24

L'année prochaine, j'aide à nettoyer plusieurs environnements SQL Server.

Nous avons environ 10 000 procédures stockées et nous estimons que seulement environ 1 000 d'entre elles sont utilisées régulièrement, et environ 200 autres sont utilisées en de rares occasions, ce qui signifie que nous avons beaucoup de travail à faire.

Étant donné que plusieurs départements et équipes peuvent accéder à ces bases de données et procédures, nous ne sommes pas toujours ceux qui appellent les procédures, ce qui signifie que nous devons déterminer quelles procédures sont appelées. En plus de cela, nous voulons déterminer cela sur quelques mois, pas sur quelques jours (ce qui élimine certaines possibilités).

Une approche consiste à utiliser SQL Server Profileret à suivre les procédures qui sont appelées et à les comparer à la liste des procédures que nous avons, tout en marquant si les procédures sont utilisées ou non. À partir de là, nous pourrions déplacer les procédures vers un schéma différent au cas où un service viendrait crier.

L' Profilerapproche la plus efficace est-elle utilisée ici? Et / Ou avez-vous fait quelque chose de similaire et trouvé une autre manière / meilleure façon de le faire?

Question3CPO
la source

Réponses:

32

Vous pouvez utiliser la trace côté serveur (différente de l'utilisation de l'interface graphique du profileur qui nécessite plus de ressources) pendant vos tests ou votre cycle commercial et capturer uniquement les éléments liés aux SP. Ensuite, vous pouvez charger cela dans un tableau ou exceller pour une analyse plus approfondie.

La deuxième approche consiste à utiliser DMV sys.dm_exec_procedure_stats (avec la limitation que si le serveur SQL est redémarré, les données sont vidées).

Vous pouvez même planifier un travail pour capturer les données DMV dans une table pour les conserver persistantes.

 -- Get list of possibly unused SPs (SQL 2008 only)
    SELECT p.name AS 'SP Name'        -- Get list of all SPs in the current database
    FROM sys.procedures AS p
    WHERE p.is_ms_shipped = 0

    EXCEPT

    SELECT p.name AS 'SP Name'        -- Get list of all SPs from the current database 
    FROM sys.procedures AS p          -- that are in the procedure cache
    INNER JOIN sys.dm_exec_procedure_stats AS qs
    ON p.object_id = qs.object_id
    WHERE p.is_ms_shipped = 0;

Faire référence à :

Kin Shah
la source
1
Voir également stackoverflow.com/questions/10421439/… et stackoverflow.com/questions/7150900/… (en ignorant que sur ce dernier le lien vers SQLServerPedia est maintenant mort).
Aaron Bertrand
2
Assurez-vous de vérifier le DMV périodiquement au cours des semaines, voire des mois, car il peut y avoir des SP qui ne fonctionnent que sur une base mensuelle, voire trimestrielle. Les DMV sont effacés lorsque l'instance est redémarrée, effacée manuellement ou même juste au fil du temps.
Kenneth Fisher
1
@KennethFisher C'est pourquoi j'ai recommandé de planifier un travail pour capturer les données DMV dans une table. Merci d'avoir mentionné cependant!
Kin Shah
11

Vous pouvez trouver cette question utile, elle s'applique aux tables et aux colonnes mais suggère d'utiliser un outil tiers ApexSQL Clean qui peut également trouver des procédures stockées inutilisées ainsi que tous les objets qui ne sont référencés par aucun autre objet dans la base de données ou dans des bases de données externes

Avertissement: je travaille pour ApexSQL en tant qu'ingénieur de support

Milica Medic
la source
3
L'OP ne veut pas trouver unreferenced stored procedures, mais OP veut trouver le SP inutilisé. Votre réponse ne sert pas de réponse à cette question.
Kin Shah
Kin je vais mettre à jour. ApexSQL Clean marque les objets inutilisés comme non référencés, donc je comprends que cela a causé la confusion
Milica Medic
10

Si vous utilisez SQL Server 2008+, vous pouvez également utiliser des événements étendus avec une cible d'histogramme . Peut-être que ce serait plus léger qu'une trace.

AFAIK, vous auriez besoin de créer une session différente pour chaque base de données d'intérêt, car je ne voyais aucune indication que le regroupement sur plusieurs colonnes était possible. L'exemple rapide ci-dessous filtre surdatabase_id=10

CREATE EVENT SESSION [count_module_start_database_10]
ON SERVER
ADD EVENT sqlserver.module_start
(  
        WHERE (source_database_id=10) 
)
ADD TARGET package0.asynchronous_bucketizer
(     SET  filtering_event_name='sqlserver.module_start', 
            source_type=0, 
            source='object_id',
            slots = 10000
)
WITH (MAX_DISPATCH_LATENCY = 5 SECONDS)
GO
ALTER EVENT SESSION [count_module_start_database_10]
ON SERVER
STATE=START

Et puis après avoir exécuté quelques procédures stockées dans cette base de données plusieurs fois et récupéré les données avec

SELECT CAST(target_data as XML) target_data
FROM sys.dm_xe_sessions AS s 
JOIN sys.dm_xe_session_targets t
    ON s.address = t.event_session_address
WHERE s.name = 'count_module_start_database_10'

La sortie est

<HistogramTarget truncated="0" buckets="16384">
  <Slot count="36">
    <value>1287675635</value>
  </Slot>
  <Slot count="3">
    <value>1271675578</value>
  </Slot>
  <Slot count="2">
    <value>1255675521</value>
  </Slot>
</HistogramTarget>

Montrant que la procédure avec object_idof a 1287675635été exécutée 36 fois par exemple. Il asynchronous_bucketizers'agit uniquement de mémoire, il est donc préférable de configurer quelque chose qui l'interroge de temps en temps et le sauvegarde dans un stockage persistant.

Martin Smith
la source
1
C'est vrai, vous avez besoin d'une session par base de données. Ce serait bien de le dire WHERE (source_database_id IN (10,15,20))mais hélas ce n'est pas supporté.
Aaron Bertrand
@AaronBertrand - Et même s'il était pris en charge, vous auriez toujours besoin de compter séparément les appels de procédure pour les objets avec le même object_id(ou le même object_name) dans différentes bases de données et je ne pense pas que ce soit possible non plus.
Martin Smith
Corrigez-moi si je me trompe mais extended eventsoù a été ajouté en 2012 et non en 2008?
Peter
1
@Peter oui vous vous trompez. :-) technet.microsoft.com/en-us/library/dd822788(v=sql.100).aspx
Martin Smith
1
L'interface utilisateur des événements étendus n'a pas été introduite avant SSMS 2012 et je ne pense pas qu'ils l'ont rendue rétrocompatible. En 2008 , la seule façon de créer des sessions sur la boîte était par TSQL mais il y avait un projet communautaire pour des fonctionnalités similaires extendedeventmanager.codeplex.com
Martin Smith
4

Comme suite au script de Kin. Voici un script simple pour créer un tableau pour suivre les utilisations dans le temps et un script pour le mettre à jour périodiquement.

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  Create the use table 
--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CREATE TABLE [dbo].[_ProcedureUseLog](
    [ObjectName] [nvarchar](255) NOT NULL,
    [UseCount] [int] NULL,
    [LastUse] [datetime] NULL,
    [LastCache] [datetime] NULL,
 CONSTRAINT [PK___PROCEDURE_USE] PRIMARY KEY CLUSTERED 
(
    [ObjectName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[_ProcedureUseLog] ADD  CONSTRAINT [DF_Table_1_References]  DEFAULT ((0)) FOR [UseCount]
GO

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  Run this periodically to update the usage stats
--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DECLARE @UsesTable TABLE
(
    ObjectName nvarchar(255),
    Executions int,
    LastUse datetime,
    LastCache datetime
)

INSERT INTO @UsesTable       
SELECT p.name, qs.execution_count, qs.last_execution_time, qs.cached_time
FROM    sys.procedures AS p LEFT OUTER JOIN
        sys.dm_exec_procedure_stats AS qs ON p.object_id = qs.object_id
WHERE        (p.is_ms_shipped = 0)

MERGE [dbo].[_ProcedureUseLog]      AS [Target]
USING @UsesTable                    AS [Source]
    ON Target.ObjectName = Source.ObjectName
WHEN MATCHED AND 
        ( Target.LastCache <> Source.LastCache)
    THEN UPDATE SET
        Target.UseCount = Target.UseCount + Source.Executions,
        Target.LastCache = Source.LastCache,
        Target.LastUse = Source.LastUse
WHEN NOT MATCHED
    THEN INSERT (ObjectName, UseCount, LastUse, LastCache) 
    VALUES      (ObjectName, Executions, LastUse, LastCache);

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  This just shows what you've logged so far
--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SELECT * FROM [_ProcedureUseLog] ORDER BY UseCount DESC
James White
la source
0

Ce message fournit également un script pour trouver des objets inutilisés: Rechercher les tables de base de données inutilisées dans SQL Server Ci-dessous le script de l'article, j'ai changé le type de table "U" en type de procédure stockée "P":

   USE DBName;
   SELECT 

       ao.[name] [Table],
       s.[name] [Schema],
       [create_date] [Created],
        [modify_date] [LastModified]
    FROM
         sys.all_objects ao JOIN sys.schemas s
           ON ao.schema_id = s.schema_id
    WHERE
         OBJECT_ID NOT IN (
              SELECT OBJECT_ID
              FROM sys.dm_db_index_usage_stats
        )
        AND [type] = 'P'
    ORDER BY
        [modify_date] DESC
Milica Medic
la source
Cela renverra toujours toutes les procédures car les procédures n'obtiennent aucune entrée dans les statistiques d'utilisation de l'index DMV ...
Martin Smith