web-dev-qa-db-ja.com

データベーステーブルがもうアクセスされているかどうかを確認するにはどうすればよいですか? 「SELECTトリガー」のようなものが欲しい

数百のテーブルを持つ非常に大きなデータベースがあり、多くの製品をアップグレードした後、それらの半分はもう使用されていないと確信しています。テーブルがアクティブに選択されているかどうかを確認するにはどうすればよいですか?プロファイラーを使用することはできません-数日以上監視したいだけでなく、数千のストアドプロシージャもあり、プロファイラーは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)
56
SqlRyan

sys.dm_db_index_usage_stats を見てください。列last_user_xxxには、ユーザー要求からテーブルに最後にアクセスした時刻が含まれます。このテーブルは、サーバーの再起動後に追跡をリセットするため、データに依存する前にしばらく実行したままにする必要があります。

40
Remus Rusanu

再:プロファイラー、 SP:StmtCompleted を監視する場合、ストアドプロシージャ内で実行されるすべてのステートメントをキャプチャし、sproc内のテーブルアクセスをキャッチします。すべてがストアドプロシージャを通過しない場合は、 SQL:StmtCompleted イベントも必要になる場合があります。

多数のイベントがあるため、トレースのサイズが原因で、長時間にわたってトレースすることはおそらく実用的ではありません。ただし、フィルターを適用できます-例TextDataには、確認するテーブルの名前が含まれます。一度にフィルター処理するテーブル名のリストを提供し、それらを徐々に処理することができます。したがって、これらのテーブルのいずれにもアクセスしていない場合、トレースイベントを取得しないでください。

あなたに適した/実行可能なアプローチではないと感じたとしても、拡張する価値があると思いました。

別の解決策は、ソースコードをグローバルに検索して、テーブルへの参照を見つけることです。ストアドプロシージャの定義をクエリして、特定のテーブルの一致を確認するか、完全なデータベーススクリプトを生成してテーブル名の検索を実行することができます。

6
AdaTheDev

SQL Server 2008の場合は、 SQL Auditing をご覧ください。これにより、テーブルの選択やファイルやイベントログへのレポートなど、多くのことを監査できます。

3
dr.

次のクエリは、クエリプランキャッシュを使用して、キャッシュ内の既存のプランのいずれかにテーブルへの参照があるかどうかを確認します。これは、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 + '%'

補足:これらのテーブルを削除することを意図している場合、データをx年間保持することを義務付ける法的義務を考慮する必要があります。

0
Erwin Smout

さまざまなテーブルのユーザー権限を使用することを念頭に置いていましたが、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;

/

その後、トレースファイルを確認できます。

0
Pentium10