Sql server 2012 使用sys.dm_exec_procedure_stats识别未使用的过程

Sql server 2012 使用sys.dm_exec_procedure_stats识别未使用的过程,sql-server-2012,Sql Server 2012,我有一个表,它每小时更新一次sys.dm_exec_procedure_stats中的数据,至今已有一年多了 如果我没有看到此表中列出的程序,我可以99%以上确定它不再使用吗?我的意思是,我知道可能会有一些疯狂的边缘案例,有人设计了一个进程来运行proc,然后立即将其从缓存中删除,这样我的进程就不会记录它的使用情况。显然,我愿意忽略那些愚蠢的边缘情况。sys.dm\u exec\u procedure\u statsDMV反映了过程缓存的当前状态。SQL Server会在删除相应的缓存项时从此D

我有一个表,它每小时更新一次sys.dm_exec_procedure_stats中的数据,至今已有一年多了


如果我没有看到此表中列出的程序,我可以99%以上确定它不再使用吗?我的意思是,我知道可能会有一些疯狂的边缘案例,有人设计了一个进程来运行proc,然后立即将其从缓存中删除,这样我的进程就不会记录它的使用情况。显然,我愿意忽略那些愚蠢的边缘情况。
sys.dm\u exec\u procedure\u stats
DMV反映了过程缓存的当前状态。SQL Server会在删除相应的缓存项时从此DMV中删除项,因此您可能会通过定期拍摄此DMV的快照而错过存储过程的执行。此外,将不会捕获带有
重新编译
的存储过程

识别所有存储过程执行的更可靠的方法是使用写入文件目标的服务器端跟踪。然后可以汇总跟踪数据并将其保存到表中

下面是支持SQL对象和PowerShell脚本的
module_end
事件的XE跟踪的DDL示例。PowerShell脚本汇总跟踪文件中存储的过程执行,并将汇总数据保存到永久表中以供分析。可以定期计划PS脚本以处理滚动跟踪文件

USE YourDatabase;

CREATE EVENT SESSION [StoredProcedureExecutions] ON SERVER
ADD EVENT sqlserver.module_end(
    WHERE ([package0].[not_equal_uint64]([source_database_id],(32767)) AND [sqlserver].[equal_i_sql_ansi_string]([object_type],'P')))
ADD TARGET package0.event_file(SET filename=N'D:\SqlTraceFiles\StoredProcedureExecutions',max_file_size=(100),max_rollover_files=(5))
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=ON);

ALTER EVENT SESSION [StoredProcedureExecutions] ON SERVER 
     STATE=START;

CREATE TABLE dbo.ModuleEndSummaryStaging(
      source_database_id smallint NOT NULL
    , object_id int NOT NULL
    , object_name sysname NOT NULL
    , execution_count int NOT NULL
    , min_timestamp datetimeoffset NOT NULL
    , max_timestamp datetimeoffset NOT NULL
    CONSTRAINT PK_ModuleEndSummaryStaging PRIMARY KEY(
          source_database_id
        , object_id
        , object_name
    )
);
GO

CREATE TABLE dbo.StoredProcedureExecutionHistory(
      DatabaseName sysname NOT NULL
    , SchemaName sysname NOT NULL
    , ObjectName sysname NOT NULL
    , source_database_id smallint NOT NULL
    , object_id int NOT NULL
    , object_name sysname NOT NULL
    , ExecutionCount bigint
    , FirstExecutionTimestamp datetimeoffset NOT NULL
    , LastExecutionTimestamp datetimeoffset NOT NULL
    , CONSTRAINT PK_StoredProcedureExecutionHistory PRIMARY KEY (
          source_database_id
        , object_id
        , object_name
        , DatabaseName
        , SchemaName
        , ObjectName)
);
GO

CREATE OR ALTER PROCEDURE dbo.MergeStoredProcedureExecutionHistory
AS
SET NOCOUNT ON;
MERGE dbo.StoredProcedureExecutionHistory AS target
USING  (
    SELECT 
          source_database_id
        , object_id
        , object_name
        , execution_count
        , min_timestamp
        , max_timestamp
        , COALESCE(DB_NAME(source_database_id), N'') AS DatabaseName
        , COALESCE(OBJECT_SCHEMA_NAME(object_id, source_database_id), N'') AS SchemaName
        , COALESCE(OBJECT_NAME(object_id, source_database_id), N'') AS ObjectName
    FROM dbo.ModuleEndSummaryStaging
    ) AS source ON
        source.source_database_id = target.source_database_id
        AND source.object_id = target.object_id
        AND source.object_name = target.object_name
        AND source.DatabaseName = target.DatabaseName
        AND source.SchemaName = target.SchemaName
        AND source.ObjectName = target.ObjectName
WHEN MATCHED THEN
    UPDATE SET
          ExecutionCount += source.execution_count
        , FirstExecutionTimestamp = CASE WHEN source.min_timestamp < target.FirstExecutionTimestamp THEN source.min_timestamp ELSE target.FirstExecutionTimestamp END
        , LastExecutionTimestamp = CASE WHEN source.max_timestamp > target.LastExecutionTimestamp THEN source.max_timestamp ELSE target.LastExecutionTimestamp END
WHEN NOT MATCHED BY TARGET THEN
    INSERT (
          DatabaseName
        , SchemaName
        , ObjectName
        , source_database_id
        , object_id
        , object_name
        , ExecutionCount
        , FirstExecutionTimestamp
        , LastExecutionTimestamp
        )
    VALUES (
          source.DatabaseName
        , source.SchemaName
        , source.ObjectName
        , source.source_database_id
        , source.object_id
        , source.object_name
        , source.execution_count
        , source.min_timestamp
        , source.max_timestamp
        );
GO
使用你的数据库;
在服务器上创建事件会话[StoredProcedureExecutions]
添加事件sqlserver.module\u结束(
其中([package0]。[not_equal_uint64]([source_database_id],(32767))和[sqlserver]。[equal_i_sql_ansi_string]([object_type],'P'))
添加目标包0.event文件(设置文件名=N'D:\SqlTraceFiles\StoredProcedureExecutions',最大文件大小=(100),最大滚动文件=(5))
使用(最大内存=4096 KB,事件保留模式=允许单个事件丢失,最大调度延迟=30秒,最大事件大小=0 KB,内存分区模式=无,跟踪因果关系=关闭,启动状态=打开);
服务器上的ALTER事件会话[StoredProcedureExecutions]
状态=开始;
创建表dbo.ModuleEndSummaryStaging(
源\u数据库\u id smallint不为空
,object_id int不为空
,对象名称sysname不为空
,执行计数int不为空
,min_时间戳日期时间偏移量不为空
,max_timestamp datetimeoffset不为空
约束PK_ModuleEndSummary暂存主键(
源\数据库\ id
,对象id
,对象名称
)
);
去
创建表dbo.StoredProcedureExecutionHistory(
数据库名称sysname不为空
,SchemaName sysname不为NULL
,ObjectName sysname不为空
,源\数据库\ id smallint不为空
,object_id int不为空
,对象名称sysname不为空
,ExecutionCount bigint
,FirstExecutionTimestamp datetimeoffset不为空
,LastExecutionTimestamp datetimeoffset不为空
,约束PK_StoredProcedureExecutionHistory主键(
源\数据库\ id
,对象id
,对象名称
,数据库名
,SchemaName
,ObjectName)
);
去
创建或更改过程dbo.MergeStoredProcedureExecutionHistory
作为
不计数;
将dbo.StoredProcedureExecutionHistory合并为目标
使用(
挑选
源\数据库\ id
,对象id
,对象名称
,执行次数
,min_时间戳
,最大时间戳
,合并(数据库名称(源数据库id),N“”)作为数据库名称
,合并(对象\模式\名称(对象\ id,源\数据库\ id),N“”)作为模式名
,合并(对象名称(对象id,源数据库id),N“”)作为对象名称
来自dbo.ModuleEndSummaryStaging
)来源于
source.source\u database\u id=target.source\u database\u id
并且source.object\u id=target.object\u id
并且source.object\u name=target.object\u name
并且source.DatabaseName=target.DatabaseName
和source.SchemaName=target.SchemaName
并且source.ObjectName=target.ObjectName
当匹配时
更新集
ExecutionCount+=source.execution\u计数
,FirstExecutionTimestamp=source.min_timestamptarget.LastExecutionTimestamp然后source.max\u timestamp ELSE target.LastExecutionTimestamp结束时的情况
当目标不匹配时
插入(
数据库名
,SchemaName
,ObjectName
,源\数据库\ id
,对象id
,对象名称
,行刑次数
,FirstExecutionTimestamp
,LastExecutionTimestamp
)
价值观(
source.DatabaseName
,source.SchemaName
,source.ObjectName
,source.source\u数据库\u id
,source.object\u id
,source.object\u name
,source.execution\u count
,source.min_时间戳
,source.max_时间戳
);
去
下面是PS脚本示例。您需要为您的系统修改引用的程序集路径(我使用的是与最新SSMS版本一起安装的程序集路径)

添加类型-路径“C:\Program Files\Microsoft SQL Server\140\Shared\Microsoft.SqlServer.XE.Core.dll”
添加类型-路径“C:\Program Files\Microsoft SQL Server\140\Shared\Microsoft.SqlServer.XEvent.Linq.dll”
#用于按源\数据库\ id、对象\ id和对象\名称汇总过程调用的实用程序类
添加类型-类型定义`
@"
使用制度;
使用System.Collections.Generic;
使用系统文本;
命名空间扩展事件性
{
公共静态类ExtendedEventsAgegator
{
public static Dictionary AggregatedEvents=new Dictionary();
公共静态void AggregateTraceFiles(字符串XefilePathPathPattern)
{
AggregatedEvents.Clear();
使用(var events=new Microsoft.SqlServer.XEvent.Linq.QueryableXEventData(xeFilePathPattern))
{
Add-Type -Path "C:\Program Files\Microsoft SQL Server\140\Shared\Microsoft.SqlServer.XE.Core.dll"
Add-Type -Path "C:\Program Files\Microsoft SQL Server\140\Shared\Microsoft.SqlServer.XEvent.Linq.dll"

# utility class to summarize proc calls by source_database_id, object_id, and object_name
Add-Type -TypeDefinition `
@"
using System;
using System.Collections.Generic;
using System.Text;

namespace ExtendedEventsUtility
{
    public static class ExtendedEventsAggegator
    {

        public static Dictionary<string, ModuleEndSummary> AggregatedEvents = new Dictionary<string, ModuleEndSummary>();

        public static void AggregateTraceFiles(string xeFilePathPattern)
        {
            AggregatedEvents.Clear();
            using (var events = new Microsoft.SqlServer.XEvent.Linq.QueryableXEventData(xeFilePathPattern))
            {
                foreach (var xe in events)
                {
                    ExtendedEventsAggegator.aggregateEvent(xe);
                }
            }
        }

        private static void aggregateEvent(Microsoft.SqlServer.XEvent.Linq.PublishedEvent eventData)
        {
            ModuleEndSummary aggregatedEvent;
            var key = new StringBuilder();
            key.Append(eventData.Fields["source_database_id"].Value.ToString());
            key.Append("|");
            key.Append(eventData.Fields["object_id"].Value.ToString());
            key.Append("|");
            key.Append(eventData.Fields["object_name"].Value.ToString());
            var keyValue = key.ToString();
            if (AggregatedEvents.ContainsKey(keyValue))
            {
                aggregatedEvent = AggregatedEvents[keyValue];
            }
            else
            {
                aggregatedEvent = new ModuleEndSummary()
                {
                    source_database_id = (UInt32)eventData.Fields["source_database_id"].Value,
                    object_id = (Int32)eventData.Fields["object_id"].Value,
                    object_name = (string)eventData.Fields["object_name"].Value
                };
                AggregatedEvents.Add(keyValue, aggregatedEvent);
            }
            aggregatedEvent.executionCount += 1;
            if((DateTimeOffset)eventData.Timestamp < aggregatedEvent.minTimestamp)
            {
                aggregatedEvent.minTimestamp = (DateTimeOffset)eventData.Timestamp;
            }
            if ((DateTimeOffset)eventData.Timestamp > aggregatedEvent.maxTimestamp)
            {
                aggregatedEvent.maxTimestamp = (DateTimeOffset)eventData.Timestamp;
            }

        }

    }

    public class ModuleEndSummary
    {
        public UInt32 source_database_id;
        public Int32 object_id;
        public string object_name;
        public Int32 executionCount = 0;
        public DateTimeOffset minTimestamp = DateTimeOffset.MaxValue;
        public DateTimeOffset maxTimestamp = DateTimeOffset.MinValue;
    }
}
"@ -ReferencedAssemblies ("C:\Program Files\Microsoft SQL Server\140\Shared\Microsoft.SqlServer.XE.Core.dll", "C:\Program Files\Microsoft SQL Server\140\Shared\Microsoft.SqlServer.XEvent.Linq.dll")

try {

    # move trace files that are not currently in use to import staging subfolder
    $sourceTraceFolderPath = "D:\SqlTraceFiles\"
    $targetTraceSubFolderPath = "D:\SqlTraceFiles\ImportStaging\"
    $traceFilePattern = "StoredProcedureExecutions*.xel"
    if(!(Test-Path $targetTraceSubFolderPath)) {
        [void](New-Item -Path $targetTraceSubFolderPath -ItemType Directory)
    }
    Get-Item "$sourceTraceFolderPath\$traceFilePattern" | Move-Item -Destination $targetTraceSubFolderPath -ErrorAction Ignore

    # aggegate usage by source_database_id, object_id, and object_name
    [ExtendedEventsUtility.ExtendedEventsAggegator]::AggregateTraceFiles("$targetTraceSubFolderPath\$traceFilePattern")

    # create data table for SqlBulkCopy
    $dt = New-Object System.Data.DataTable
    [void]$dt.Columns.Add("source_database_id", [System.Type]::GetType("System.Int16"))
    [void]$dt.Columns.Add("object_id", [System.Type]::GetType("System.Int32"))
    [void]$dt.Columns.Add("object_name", [System.Type]::GetType("System.String"))
    [void]$dt.Columns.Add("execution_count", [System.Type]::GetType("System.Int32"))
    [void]$dt.Columns.Add("min_timestamp", [System.Type]::GetType("System.DateTimeOffset"))
    [void]$dt.Columns.Add("max_timestamp", [System.Type]::GetType("System.DateTimeOffset"))

    # load proc execution summary into data table
    foreach ($proc in [ExtendedEventsUtility.ExtendedEventsAggegator]::AggregatedEvents.Values) {
        $row = $dt.NewRow()
        $dt.Rows.Add($row)
        $row["source_database_id"] = $proc.source_database_id
        $row["object_id"] = $proc.object_id
        $row["object_name"] = $proc.object_name
        $row["execution_count"] = $proc.executioncount
        $row["min_timestamp"] = $proc.mintimestamp
        $row["max_timestamp"] = $proc.maxtimestamp
    }

    # bulk insert execution summary into staging table
    $connectionString = "Data Source=.;Integrated Security=SSPI;Initial Catalog=YourDatabase"
    $connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
    $command = New-Object System.Data.SqlClient.SqlCommand("TRUNCATE TABLE dbo.ModuleEndSummaryStaging;", $connection)
    $connection.Open()
    [void]$command.ExecuteNonQuery()
    $connection.Close()
    $bcp = New-Object System.Data.SqlClient.SqlBulkCopy($connectionString)
    $bcp.DestinationTableName = "dbo.ModuleEndSummaryStaging"
    $bcp.WriteToServer($dt);
    $bcp.Dispose()

    # merge proc execution summary into history table
    $connection.Open()
    $command.CommandText="dbo.MergeStoredProcedureExecutionHistory"
    $command.CommandType = [System.Data.CommandType]::StoredProcedure
    [void]$command.ExecuteNonQuery()
    [void]$connection.Close()

    #delete files after import
    Get-ChildItem "$targetTraceSubFolderPath\$traceFilePattern" | Remove-Item

} catch {
    throw
}