0

一部のテーブルで変更された列を監査テーブルに登録する簡単/一般的な方法が見つかりません。

この方法で更新後にトリガーを使用してそれを実行しようとしました:

まず、監査テーブルの定義:

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

問題点

  1. exec 関数を使用すると、Inserted と Deleted でコンテキストが失われました
  2. colnumber 常に相関数であるとは限らないようです。20列のテーブルを作成し、1つを削除して別のテーブルを作成すると、最後の列には数値> @colcountがあるようです

ネット上で解決策を探していましたが、わかりませんでした

何か案が?

ありがとう!

4

3 に答える 3

1

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

動的 SQL を使用する場合は、それを使用してスクリプトを作成し、トリガーを作成します。そして、監査したいテーブルごとに監査テーブルを作成します (実際にはすべてのテーブルに 2 つ用意されています)。そうしないと、「すべてを支配する 1 つのテーブル」をロックするためにパフォーマンスの問題が発生します。

于 2011-10-07T22:13:26.157 に答える
1

@Santiago : それでも動的 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(@sql)次に、行を次のように置き換えますINSERT INTO #stmt (SQL) VALUES (@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

@colcount int4 行目とその前のコンマを削除します。情報スキーマの選択を削除します。

于 2011-10-07T22:07:37.267 に答える
1

これは、構造の選択に関するより大きな問題を浮き彫りにしています。セットベースのソリューションを作成してみてください。ループと動的 SQL を削除し、Audit 行を挿入する単一のステートメントを記述します。可能ですが、すべての列を分割するのではなく 1 行に保持するなど、別のテーブル レイアウトを検討しやすくすることができます。

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

SELECT column_id FROM sys.columns WHERE object_id = OBJECT_ID(DB_NAME()+'.dbo.Table'); 
于 2011-10-06T23:52:31.780 に答える