Identificación de procedimientos almacenados no utilizados

24

El próximo año, estoy ayudando en un esfuerzo por limpiar varios entornos de SQL Server.

Tenemos alrededor de 10,000 procedimientos almacenados y estimamos que solo alrededor de 1000 de ellos se usan de manera regular, y otros 200 más o menos se usan en una rara ocasión, lo que significa que tenemos mucho trabajo por hacer.

Dado que tenemos varios departamentos y equipos que pueden acceder a estas bases de datos y procedimientos, no siempre somos los que llamamos a los procedimientos, lo que significa que debemos determinar qué procedimientos se están llamando. Además de eso, queremos determinar esto en unos pocos meses, no en unos pocos días (lo que elimina algunas posibilidades).

Un enfoque para esto es usar SQL Server Profilery rastrear qué procedimientos se están llamando y compararlos con la lista de los procedimientos que tenemos, mientras marcamos si los procedimientos se usan o no. A partir de entonces, podríamos mover los procedimientos a un esquema diferente en caso de que un departamento venga gritando.

¿Está usando el Profilerenfoque más efectivo aquí? ¿Y / o alguno de ustedes hizo algo similar y encontró otra forma / mejor manera de hacerlo?

Pregunta3CPO
fuente

Respuestas:

32

Puede usar el rastreo del lado del servidor (diferente del uso de la GUI de Profiler que incurre en más recursos) durante sus pruebas o su ciclo comercial y capturar solo cosas relacionadas con los SP. Luego puede cargar eso en una tabla o sobresalir para un análisis posterior.

El segundo enfoque es utilizar DMV sys.dm_exec_procedure_stats (con la limitación de que si el servidor sql se reinicia, los datos se vacían).

Incluso puede programar un trabajo para capturar datos del DMV en una tabla para mantenerlos persistentes.

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

Referirse a :

Kin Shah
fuente
1
También vea stackoverflow.com/questions/10421439/… y stackoverflow.com/questions/7150900/… (ignorando que en este último el enlace a SQLServerPedia ahora está muerto).
Aaron Bertrand
2
Asegúrese de verificar el DMV periódicamente en el transcurso de semanas o incluso meses, ya que puede haber SP que solo se ejecutan mensualmente o incluso trimestralmente. Los DMV se borran cuando la instancia se reinicia, se borra manualmente o incluso con el tiempo.
Kenneth Fisher
1
@KennethFisher Es por eso que recomendé programar un trabajo para capturar datos del DMV en una tabla. ¡Gracias por mencionarlo!
Kin Shah
11

Puede encontrar esta pregunta útil, se aplica a tablas y columnas, pero sugiere utilizar una herramienta de terceros ApexSQL Clean, que también puede encontrar procedimientos almacenados no utilizados, así como todos los objetos a los que no hace referencia ningún otro objeto en la base de datos o en bases de datos externas

Descargo de responsabilidad: trabajo para ApexSQL como ingeniero de soporte

Milica Medic
fuente
3
El OP no quiere encontrar unreferenced stored procedures, en cambio OP quiere encontrar el SP no utilizado. Su respuesta no sirve como respuesta para esta pregunta.
Kin Shah
Kin lo actualizaré. ApexSQL Clean marca los objetos no utilizados como no referenciados, así que entiendo que eso causó la confusión
Milica Medic
10

Si está utilizando SQL Server 2008+, también puede usar eventos extendidos con un objetivo de histograma . Posiblemente esto sería más ligero que un rastro.

AFAIK necesitaría crear una sesión diferente para cada base de datos de interés, ya que no pude ver ninguna indicación de que fuera posible agrupar en varias columnas. El ejemplo rápido a continuación se filtra endatabase_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

Y luego, después de ejecutar algunos procedimientos almacenados en esa base de datos varias veces y recuperar los datos con

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 salida es

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

Mostrando que el procedimiento con object_idde 1287675635se ejecutó 36 veces, por ejemplo. El asynchronous_bucketizeres la memoria solamente por lo que sería mejor para establecer algo que las encuestas esta vez en cuando y lo guarda en un almacenamiento persistente.

Martin Smith
fuente
1
Es cierto, necesita una sesión por base de datos. Sería genial decirlo WHERE (source_database_id IN (10,15,20))pero, por desgracia, esto no es compatible.
Aaron Bertrand
@AaronBertrand: e incluso si fuera compatible, aún tendría que contar las llamadas a procedimientos para objetos con el mismo object_id(o el mismo object_name) en diferentes bases de datos por separado y tampoco creo que sea posible.
Martin Smith
Corrígeme si estoy equivocado, pero ¿ extended eventsdónde se agregó en 2012, no en 2008?
Peter
1
@ Peter, sí, te equivocas. :-) technet.microsoft.com/en-us/library/dd822788(v=sql.100).aspx
Martin Smith
1
La IU de eventos extendidos no se introdujo hasta SSMS 2012 y no creo que la hicieran compatible con versiones anteriores. En 2008, la única forma de crear sesiones fuera de la caja era a través de TSQL, aunque había un proyecto comunitario para una funcionalidad similar extendedeventmanager.codeplex.com
Martin Smith
4

Como continuación del guión de Kin. Aquí hay un script simple para crear una tabla para rastrear los usos a lo largo del tiempo y un script para actualizarlo periódicamente.

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  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
fuente
0

Esta publicación también proporciona una secuencia de comandos para encontrar objetos no utilizados: busque las tablas de la base de datos no utilizadas en SQL Server A continuación se muestra la secuencia de comandos del artículo, cambié el tipo de tabla "U" en el tipo de procedimiento almacenado "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
fuente
Esto siempre devolverá todos los procedimientos ya que los procedimientos no obtienen entradas realizadas en las estadísticas de uso del índice DMV ...
Martin Smith