65

我有一个非常大的数据库,其中包含数百个表,经过多次产品升级,我确信其中有一半不再使用。我如何判断一个表是否正在被主动选择?我不能只使用 Profiler——我不仅要观察几天以上,而且还有成千上万的存储过程,而且 Profiler 不会将 SP 调用转换为表访问调用。

我唯一能想到的就是在感兴趣的表上创建一个聚集索引,然后监视聚集索引上sys.dm_db_index_usage_stats是否有任何搜索或扫描,这意味着表中的数据已加载。但是,在每个表上添加聚集索引是一个坏主意(出于多种原因),因为它实际上并不可行。

我还有其他选择吗?我一直想要一个像“SELECT 触发器”这样的功能,但可能还有其他原因导致 SQL Server 也没有这个功能。

解决方案:

谢谢,Remus,为我指明了正确的方向。使用这些列,我创建了以下 SELECT,这正是我想要的。

  WITH LastActivity (ObjectID, LastAction) AS 
  (
       SELECT object_id AS TableName,
              last_user_seek as LastAction
         FROM sys.dm_db_index_usage_stats u
        WHERE database_id = db_id(db_name())
        UNION 
       SELECT object_id AS TableName,
              last_user_scan as LastAction
         FROM sys.dm_db_index_usage_stats u
        WHERE database_id = db_id(db_name())
        UNION
       SELECT object_id AS TableName,
              last_user_lookup as LastAction
         FROM sys.dm_db_index_usage_stats u
        WHERE database_id = db_id(db_name())
  )
  SELECT OBJECT_NAME(so.object_id) AS TableName,
         MAX(la.LastAction) as LastSelect
    FROM sys.objects so
    LEFT
    JOIN LastActivity la
      on so.object_id = la.ObjectID
   WHERE so.type = 'U'
     AND so.object_id > 100
GROUP BY OBJECT_NAME(so.object_id)
ORDER BY OBJECT_NAME(so.object_id)
4

6 回答 6

48

查看sys.dm_db_index_usage_stats。last_user_xxx 列将包含上次从用户请求访问该表的时间。此表在服务器重新启动后重置其跟踪,因此您必须让它运行一段时间才能依赖其数据。

于 2010-01-28T16:19:36.090 回答
6

回复:Profiler,如果您监视SP:StmtCompleted,它将捕获在存储过程中执行的所有语句,以便捕获存储过程中的表访问。如果不是所有内容都通过存储过程,您可能还需要SQL:StmtCompleted事件。

将有大量事件,因此由于跟踪的大小,长时间跟踪可能仍然不切实际。但是,您可以应用过滤器 - 例如,其中 TextData 包含您要检查的表的名称。您可以在任何时候提供一个表名列表以进行过滤并逐步处理它们。因此,如果这些表都没有被访问过,您不应该得到任何跟踪事件。

即使你觉得这对你来说不是一个合适/可行的方法,我认为它值得扩展。

另一种解决方案是对源代码进行全局搜索以查找对表的引用。您可以查询存储过程定义以检查给定表的匹配项,或者只生成一个完整的数据库脚本并对表名进行查找。

于 2010-01-28T15:44:44.347 回答
3

对于 SQL Server 2008,您应该查看SQL Auditing。这使您可以审核许多事情,包括对表的选择以及对文件或事件日志的报告。

于 2010-01-28T15:35:07.233 回答
1

以下查询使用查询计划缓存来查看缓存中的任何现有计划中是否存在对表的引用。这不能保证 100% 准确(因为如果存在内存限制,查询计划会被清除),但可以用来获得有关表使用的一些见解。

SELECT schema_name(schema_id) as schemaName, t.name as tableName,
    databases.name,
dm_exec_sql_text.text AS TSQL_Text,
dm_exec_query_stats.creation_time, 
dm_exec_query_stats.execution_count,
dm_exec_query_stats.total_worker_time AS total_cpu_time,
dm_exec_query_stats.total_elapsed_time, 
dm_exec_query_stats.total_logical_reads, 
dm_exec_query_stats.total_physical_reads, 
dm_exec_query_plan.query_plan
FROM sys.dm_exec_query_stats 
CROSS APPLY sys.dm_exec_sql_text(dm_exec_query_stats.plan_handle)
CROSS APPLY sys.dm_exec_query_plan(dm_exec_query_stats.plan_handle)
INNER JOIN sys.databases ON dm_exec_sql_text.dbid = databases.database_id
RIGHT JOIN sys.tables t (NOLOCK) ON cast(dm_exec_query_plan.query_plan as varchar(max)) like '%' + t.name + '%'
于 2019-05-21T18:08:35.360 回答
0

我曾考虑使用不同表的用户权限,但后来我记得您可以使用 ON LOGON 触发器打开跟踪,您可能会从中受益:

CREATE OR REPLACE TRIGGER SYS.ON_LOGON_ALL

AFTER LOGON ON DATABASE
WHEN (

USER 'MAX'

)
BEGIN

EXECUTE IMMEDIATE 'ALTER SESSION SET SQL_TRACE TRUE';

--EXECUTE IMMEDIATE 'alter session set events ''10046 trace name context forever level 12''';

EXCEPTION

WHEN OTHERS THEN

NULL;

END;

/

然后您可以检查您的跟踪文件。

于 2010-01-28T16:07:56.550 回答
-1

这个解决方案对我来说比上面的解决方案更好。但是,仍然限制了服务器也没有重新启动,但仍然可以让您很好地了解未使用的表。

SELECT [name]
      ,[object_id]
      ,[principal_id]
      ,[schema_id]
      ,[parent_object_id]
      ,[type]
      ,[type_desc]
      ,[create_date]
      ,[modify_date]
      ,[is_ms_shipped]
      ,[is_published]
      ,[is_schema_published]
  FROM [COMTrans].[sys].[all_objects]
  where object_id not in (
select object_id from sys.dm_db_index_usage_stats

)
and type='U'
order by name
于 2013-03-27T13:20:26.920 回答