2011-10-06 16 views
0

一部のテーブルで列が変更された監査テーブルに登録する簡単で一般的な方法が見つかりません。すべての監査表の定義の監査の更新時にSQLサーバートリガー

まず:

CREATE TABLE [Audit](
[Id] [int] IDENTITY(1,1) NOT NULL, 
[Date] [datetime] NOT NULL default GETDATE(), 
[IdTypeAudit] [int] NOT NULL, --2 for Modify 
[UserName] [varchar](50) NULL, 
[TableName] [varchar](50) NOT NULL, 
[ColumnName] [varchar](50) NULL, 
[OldData] [varchar](50) NULL, 
[NewData] [varchar](50) NULL) 

次のいずれかのテーブル内のAFTER UPDATEのトリガー

私はこのようにアップデートした後にトリガを使用して、それを実行しようとしました。

DECLARE 
    @sql varchar(8000), 
    @col int, 
    @colcount int 

select @colcount = count(*) from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'MyTable' 
set @col = 1 

while(@col < @colcount) 
begin 

    set @sql= 
    'INSERT INTO Audit 
    SELECT 2, UserNameLastModif, ''MyTable'', COL_NAME(Object_id(''MyTable''), '+ convert(varchar,@col) +'), Deleted.' 
    + COL_NAME(Object_id('MyTable'), @col) + ', Inserted.' + COL_NAME(Object_id('MyTable'), @col) + ' 
    FROM Inserted LEFT JOIN Deleted ON Inserted.[MyTableId] = Deleted.[MyTableId] 
    WHERE COALESCE(Deleted.' + COL_NAME(Object_id('MyTable'), @col) + ', '''') <> COALESCE(Inserted.' + COL_NAME(Object_id('MyTable'), @col) + ', '''')' 

    --UserNameLastModif is an optional column on MyTable 
    exec(@sql) 
    set @col = @col + 1 

end 

問題

私はexec関数を使用する場合
  1. 挿入、削除は
  2. コンテキストを失ったそれは常に相関数ではありませんcolnumberあなたは20列を持つテーブルを作成し、1を削除して、別のものを作成した場合、思われることだが、最後の一つは、私がネットの上にすべてのためのソリューションを探していた番号> @colcount

を持っているが、私は

任意のアイデアを把握couln't?

ありがとうございます!

答えて

1

これは、構造上の選択に関する大きな問題を強調しています。セットベースのソリューションを作成してみてください。ループと動的SQLを削除し、監査行を挿入する単一の文を記述します。可能ですが、すべての列を分割する代わりに1行に保持するなど、異なる表レイアウトを簡単に検討することは可能です。

SQL 2000では、syscolumnsを使用します。 SQL 2005+では、sys.columnsを使用します。すなわち

SELECT column_id FROM sys.columns WHERE object_id = OBJECT_ID(DB_NAME()+'.dbo.Table'); 
+0

私はあなたが言った解決策を理解していますが、監査する必要があるテーブルごとに監査テーブルが必要なため、データベース全体で変更を探す複雑さが増しています。多分私はあなたに勧めます。 – Santiago

+1

@Sanitagoでは、各テーブルの監査テーブルが必要です。そうしないとロックの問題が発生します。 – HLGEM

+0

@HLGEM:非常に良い点。私が書いた最近のトリガーは、サービスブローカーを使用します(監査用ではありませんが、それは可能な解決策です)。ロックが解放されるまで非同期にキューに入れられます。あなたが多くの努力をしているのであれば、別々のテーブルを持たないといけません。彼らはまた、より高速にクエリします。 –

1

@サンティアゴ:まだ動的SQLで記述したい場合は、すべての文を最初に準備してから実行する必要があります。 8000文字ですべての文で十分でない場合があります。良い解決策は、テーブルを使用してそれらを格納することです。

IF NOT OBJECT_ID('tempdb..#stmt') IS NULL 
    DROP TABLE #stmt; 
CREATE TABLE #stmt (ID int NOT NULL IDENTITY(1,1), SQL varchar(8000) NOT NULL); 

次いで各行をexecしINSERT INTO #stmt (SQL) VALUES (@sql);

に沿っexec(@sql)を置き換えます。

WHILE EXISTS (SELECT TOP 1 * FROM #stmt) 
BEGIN 
    BEGIN TRANSACTION; 
     EXEC (SELECT TOP 1 SQL FROM #stmt ORDER BY ID); 
     DELETE FROM #stmt WHERE ID = (SELECT MIN(ID) FROM #stmt); 
    COMMIT TRANSACTION; 
END 

カラムループにはsys.columnsを使用してください(SQL 2005/2008を使用すると仮定します)。

SET @col = 0; 
WHILE EXISTS (SELECT TOP 1 * FROM sys.columns WHERE object_id = OBJECT_ID('MyTable') AND column_id > @col) 
BEGIN 
    SELECT TOP 1 @col = column_id FROM sys.columns 
    WHERE object_id = OBJECT_ID('MyTable') AND column_id > @col ORDER BY column_id ASC; 
    SET @sql .... 
    INSERT INTO #stmt .... 
END 

行4を削除します。@colcount intと先行するカンマ。情報スキーマの選択を削除します。

1

トリガーをループさせることはありません。動的SQlを使用したり、ストアドプロシージャを呼び出したり、電子メールを送信したりしないでください。これらの事柄のすべては、トリガーでは非常に不適切です。

動的SQLを使用する場合は、スクリプトを作成してトリガーを作成します。また、監査対象のすべてのテーブル(実際にはすべてのテーブルに対して2つ)に対して監査テーブルを作成するか、「1つのテーブルですべてを統制する」ためにパフォーマンスの問題が発生します。

+0

あなたと同意して、私はどちらの動的SQLもループを使用したくないのですが、私の問題は、トリガーができるような、非妨害的な方法でカラムによる変更カラムを保存する方法を見つけることができないということです。 – Santiago

関連する問題