Identificando procedimentos armazenados não utilizados


24

No próximo ano, estou ajudando um esforço para limpar vários ambientes do SQL Server.

Temos cerca de 10.000 procedimentos armazenados e estimamos que apenas cerca de 1000 sejam usados ​​regularmente e outros 200 sejam usados ​​em raras ocasiões, o que significa que temos muito trabalho a fazer.

Como temos vários departamentos e equipes que podem acessar esses bancos de dados e procedimentos, nem sempre somos os que chamam os procedimentos, o que significa que devemos determinar como os procedimentos estão sendo chamados. Além disso, queremos determinar isso em alguns meses, não em alguns dias (o que elimina algumas possibilidades).

Uma abordagem para isso é usar SQL Server Profilere rastrear quais procedimentos estão sendo chamados e compará-los com a lista de quais procedimentos temos, marcando se os procedimentos são usados ​​ou não. A partir de então, poderíamos mudar os procedimentos para um esquema diferente, caso um departamento grite.

Está usando a Profilerabordagem mais eficaz aqui? E / ou algum de vocês fez algo semelhante e encontrou outra maneira / melhor maneira de fazer isso?

Respostas:


32

Você pode usar o rastreamento do lado do servidor (diferente da GUI do Profiler que incorre em mais recursos) durante os testes ou o ciclo de negócios e capturar apenas itens relacionados aos SPs. Em seguida, você pode carregá-lo em uma tabela ou excel para análises adicionais.

A segunda abordagem é usar o DMV sys.dm_exec_procedure_stats (com a limitação de que, se o servidor sql for reiniciado, os dados serão liberados).

Você pode até agendar um trabalho para capturar dados DMV em uma tabela para mantê-los 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;

Referir-se :


11
Consulte também stackoverflow.com/questions/10421439/… e stackoverflow.com/questions/7150900/… (ignorando que neste último o link para SQLServerPedia agora está morto).
Aaron Bertrand

2
Verifique a DMV periodicamente ao longo de semanas ou até meses, pois pode haver SPs que só são executados mensalmente ou até trimestralmente. As DMVs são limpas quando a instância é reiniciada, limpa manualmente ou mesmo com o tempo.
25913 Kenneth Fisher

11
@KennethFisher É por isso que eu recomendei agendar um trabalho para capturar dados DMV em uma tabela. Obrigado por mencionar!
Kin Shah

11

Você pode encontrar essa pergunta útil, ela se aplica a tabelas e colunas, mas sugere o uso de uma ferramenta de terceiros ApexSQL Clean, que também pode encontrar procedimentos armazenados não utilizados, bem como todos os objetos que não são referenciados por nenhum outro objeto no banco de dados ou em bancos de dados externos

Isenção de responsabilidade: trabalho para o ApexSQL como engenheiro de suporte


3
O OP não deseja encontrar unreferenced stored procedures, em vez disso, deseja encontrar o SP não utilizado. Sua resposta não serve como resposta para esta pergunta.
Kin Shah

Kin eu vou atualizar. ApexSQL Clean marca objetos não utilizados como não referenciados, então eu entendo que isso causou confusão
Milica Medic

10

Se você estiver no SQL Server 2008 ou superior, também poderá usar eventos estendidos com um destino de histograma . Possivelmente isso seria mais leve do que um traço.

No AFAIK, você precisaria criar uma sessão diferente para cada banco de dados de interesse, já que eu não conseguia ver nenhuma indicação de que o agrupamento em várias colunas fosse possível. O exemplo rápido abaixo filtradatabase_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

E depois de executar alguns procedimentos armazenados nesse banco de dados algumas vezes e recuperar os dados com

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'

A saída é

<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 o procedimento com object_idde 1287675635foi executado 36 vezes, por exemplo. A asynchronous_bucketizermemória é apenas, portanto, seria melhor configurar algo que controla isso de vez em quando e salva o armazenamento persistente.


11
É verdade, você precisa de uma sessão por banco de dados. Seria ótimo dizer, WHERE (source_database_id IN (10,15,20))mas infelizmente isso não é suportado.
Aaron Bertrand

@AaronBertrand - E mesmo que fosse suportado, você ainda precisaria contar chamadas de procedimento para objetos com o mesmo object_id(ou mesmo object_name) em diferentes bancos de dados separadamente e também não acho que isso seja possível.
Martin Smith

Corrija-me se estiver errado, mas extended eventsonde foi adicionado em 2012 e não em 2008?
Peter


11
A interface do usuário de eventos estendidos não foi introduzida até o SSMS 2012 e não acho que eles tenham sido compatíveis com versões anteriores. Em 2008, a única maneira de criar sessões fora da caixa foi através TSQL que houvesse um projeto da comunidade para funcionalidade semelhante extendedeventmanager.codeplex.com
Martin Smith

4

Como acompanhamento do roteiro de Kin. Aqui está um script simples para criar uma tabela para rastrear os usos ao longo do tempo e um script para atualizá-lo periodicamente.

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

0

Esta postagem também fornece um script para localizar objetos não utilizados: Encontre as tabelas de banco de dados não utilizadas no SQL Server Abaixo está o script do artigo, alterei o tipo de tabela "U" para o tipo de procedimento armazenado "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

Isso sempre vai retornar todos os procedimentos como procedimentos não recebem inscrições feitas no uso do índice Status de DMV ...
Martin Smith
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.