如何判断是否正在访问数据库表?想要像“SELECT触发器”这样的东西

时间:2010-01-28 15:25:34

标签: sql-server database-design sql-server-2008 clustered-index

我有一个包含数百个表的非常大的数据库,经过多次,许多产品升级后,我确信其中一半不再使用了。如何判断是否正在从中选择一个表?我不能只使用Profiler - 我不仅要观看超过几天,而且还有数千个存储过程,并且探查器不会将SP调用转换为表访问调用。

我唯一能想到的是在感兴趣的表上创建聚簇索引,然后监视sys.dm_db_index_usage_stats以查看聚簇索引上是否有任何搜索或扫描,这意味着来自表已加载。但是,在每个表上添加聚簇索引是一个坏主意(由于许多原因),因为这是不可行的。

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

SOLUTION:

谢谢,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)

7 个答案:

答案 0 :(得分:40)

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

答案 1 :(得分:6)

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

会有大量事件因此由于跟踪的大小而长时间跟踪可能仍然不实用。但是,您可以应用过滤器 - 例如其中TextData包含要检查的表的名称。您可以在任何时候提供要过滤的表名列表,并逐步完成它们。因此,如果没有访问这些表,则不应该获得任何跟踪事件。

即使你认为这对你来说不合适/可行,我认为值得继续扩展。

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

答案 2 :(得分:3)

对于SQL Server 2008,您应该查看SQL Auditing。这允许您审核许多内容,包括在表上选择并报告到文件或事件日志。

答案 3 :(得分: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 + '%'

答案 4 :(得分:0)

附注:如果您打算放弃这些表格,您可能必须考虑法律义务,这些义务会将数据保密至x年。

答案 5 :(得分: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;

/

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

答案 6 :(得分:-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