识别未使用的存储过程


24

明年,我正在帮助清理几个SQL Server环境。

我们大约有10,000个存储过程,并且估计只有大约1000个存储过程是定期使用的,而另外200个左右的存储过程很少使用,这意味着我们有很多工作要做。

由于我们有多个部门和团队可以访问这些数据库和过程,因此我们并不总是调用这些过程的人,这意味着我们必须确定正在调用的过程。最重要的是,我们希望在几个月而不是几天内确定这一点(这消除了一些可能性)。

一种解决方法是使用SQL Server Profiler和跟踪正在调用的程序,并将它们与我们拥有的程序列表进行比较,同时标记是否使用了这些程序。从那时起,我们可以将程序转移到其他模式,以防部门尖叫。

Profiler在这里使用最有效的方法吗?和/或你们有没有做过类似的事情并找到了另一种方法/更好的方法呢?

Answers:


32

您可以 在测试或业务周期中使用服务器端跟踪(与使用探查器GUI有所不同,后者会占用更多资源),并且仅捕获与SP相关的内容。然后,您可以将其加载到表格或excel中进行进一步分析。

第二种方法是使用DMV sys.dm_exec_procedure_stats(但有限制,如果重新启动sql server,则将刷新数据)。

您甚至可以安排作业以将DMV数据捕获到表中以使其持久化。

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

参考:


1
另请参见stackoverflow.com/questions/10421439/…stackoverflow.com/questions/7150900/…(忽略了后者上与SQLServerPedia的链接现在已失效)。
亚伦·伯特兰

2
确保您在数周甚至数月的时间内定期检查DMV,因为可能有一些SP仅每月或每季度运行一次。重新启动实例时,将清除DMV,手动将其清除,甚至随时间推移也会清除DMV。
肯尼斯·费舍尔

1
@KennethFisher这就是为什么我建议安排一个作业以将DMV数据捕获到表中的原因。感谢您的提及!
Kin Shah

11

您会发现此问题很有用,它适用于表和列,但建议使用第三方工具ApexSQL Clean,该工具还可以查找未使用的存储过程以及数据库或外部数据库中未由任何其他对象引用的所有对象

免责声明:我是ApexSQL的支持工程师


3
OP不想找到unreferenced stored procedures,相反,OP想找到未使用的SP。您的答案不能用作该问题的答案。
Kin Shah

亲爱的,我会更新。ApexSQL Clean将未使用的对象标记为未引用,因此我知道这引起了混淆
Milica Medic 2014年

10

如果您使用的是SQL Server 2008+,则还可以将扩展事件与直方图目标一起使用。可能这将比痕迹更轻。

AFAIK您需要为每个感兴趣的数据库创建一个不同的会话,尽管我看不到任何迹象表明可以对多列进行存储。下面的快速示例过滤了database_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

然后在该数据库中运行一些存储过程几次,然后使用

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'

输出是

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

示出了与该过程object_id1287675635被执行例如36次。的asynchronous_bucketizer不仅是记忆,因此最好的办法是设立一些调查这个每隔一段时间,并保存到永久存储。


1
的确,每个数据库需要一个会话。很好说,WHERE (source_database_id IN (10,15,20))但是a支持。
亚伦·伯特兰

@AaronBertrand-即使支持,您仍然需要分别计算在不同数据库中具有相同object_id(或相同object_name)对象的过程调用,我也不认为这是可能的。
马丁·史密斯

如果我说错了extended events,请纠正我,但在2012年而不是2008年添加的地方?
彼得


1
扩展事件UI直到SSMS 2012才引入,我不认为它们使它向后兼容。在2008年,开箱即用地创建会话的唯一方法是通过TSQL,尽管有一个用于类似功能的社区项目extendedeventmanager.codeplex.com
Martin Smith,

4

作为Kin脚本的后续内容。这是一个简单的脚本,用于创建一个表来跟踪一段时间内的使用情况,以及一个脚本来定期对其进行更新。

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

这篇文章还提供了一个脚本来查找未使用的对象:在SQL Server中查找未使用的数据库表 下面是本文中的脚本,我将表类型“ U”更改为存储过程类型“ 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

这将始终返回所有过程,因为过程不会在索引使用情况统计DMV中获得输入…
Martin Smith
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.