web-dev-qa-db-ja.com

監査トリガーを作成するProcを実行するカーソル

それで、私はこのことと数時間戦い続けて、少しでも助けを求めて手を差し伸べると思いました。データベースのすべてのテーブルに監査トリガーを作成する必要があります(この監査トリガーは機能し、テスト済みです)。各テーブルを手動で展開してスクリプトを作成するのではなく、簡単に展開する方法を見つけようとしています。

これがトリガーを作成するために作成したprocです

alter PROCEDURE [dbo].[InsertTRIGGER] 
(@triggerTable varchar(max))

AS 
DECLARE @sql nvarchar(max)
SET @sql = ('

    CREATE TRIGGER [dbo].[TR_@triggertable_AUDIT] ON [dbo].[@triggertable] FOR UPDATE, INSERT, DELETE
AS


DECLARE @bit INT ,
       @field INT ,
       @maxfield INT ,
       @char INT ,
       @fieldname VARCHAR(128) ,
       @TableName VARCHAR(128) ,
       @PKCols VARCHAR(1000) ,
       @sql VARCHAR(2000), 
       @UpdateDate VARCHAR(21) ,
       @UserName VARCHAR(128) ,
       @Type CHAR(1) ,
       @PKSelect VARCHAR(1000),
       @UserGroup VARCHAR(128),



IF IS_MEMBER (''DbEditAudit'') = 1
BEGIN
--You will need to change @TableName to match the table to be audited. 
-- Here we made GUESTS for your example.
SELECT @TableName = ''@triggertable''

-- date and user
SELECT         @UserName = SYSTEM_USER ,
       @UpdateDate = CONVERT (NVARCHAR(30),GETDATE(),126)


-- Action
IF EXISTS (SELECT * FROM inserted)
       IF EXISTS (SELECT * FROM deleted)
               SELECT @Type = ''U''
       ELSE
               SELECT @Type = ''I''
ELSE
       SELECT @Type = ''D''

-- get list of columns
SELECT * INTO #ins FROM inserted
SELECT * INTO #del FROM deleted

-- Get primary key columns for full outer join
SELECT @PKCols = COALESCE(@PKCols + '' and'', '' on'') 
               + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME
       FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,

              INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
       WHERE   pk.TABLE_NAME = @TableName
       AND     CONSTRAINT_TYPE = ''PRIMARY KEY''
       AND     c.TABLE_NAME = pk.TABLE_NAME
       AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

-- Get primary key select for insert
SELECT @PKSelect = COALESCE(@PKSelect+''+'','''') 
       + ''''''<'' + COLUMN_NAME 
       + ''=''''+convert(varchar(100),
coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + '')) + ''''>''''''
       FROM    INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ,
               INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
       WHERE   pk.TABLE_NAME = @TableName
       AND     CONSTRAINT_TYPE = ''PRIMARY KEY''
       AND     c.TABLE_NAME = pk.TABLE_NAME
       AND     c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME

IF @PKCols IS NULL
BEGIN
       RAISERROR(''no PK on table %s'', 16, -1, @TableName)
       RETURN
END

SELECT         @field = 0, 
       @maxfield = MAX(ORDINAL_POSITION) 
       FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName
WHILE @field < @maxfield
BEGIN
       SELECT @field = MIN(ORDINAL_POSITION) 
               FROM INFORMATION_SCHEMA.COLUMNS 
               WHERE TABLE_NAME = @TableName 
               AND ORDINAL_POSITION > @field
       SELECT @bit = (@field - 1 )% 8 + 1
       SELECT @bit = POWER(2,@bit - 1)
       SELECT @char = ((@field - 1) / 8) + 1
       IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @bit > 0
                                       OR @Type IN (''I'',''D'')
       BEGIN
               SELECT @fieldname = COLUMN_NAME 
                       FROM INFORMATION_SCHEMA.COLUMNS 
                       WHERE TABLE_NAME = @TableName 
                       AND ORDINAL_POSITION = @field
               SELECT @sql = ''
insert Audit (    Type, 
               TableName, 
               PK, 
               FieldName, 
               OldValue, 
               NewValue, 
               UpdateDate, 
               UserName)
select '''''' + @Type + '''''',''''''
       + @TableName + '''''','' + @PKSelect
       + '','''''' + @fieldname + ''''''''
       + '',convert(varchar(1000),d.'' + @fieldname + '')''
       + '',convert(varchar(1000),i.'' + @fieldname + '')''
       + '','''''' + @UpdateDate + ''''''''
       + '','''''' + @UserName + ''''''''
       + '' from #ins i full outer join #del d''
       + @PKCols
       + '' where i.'' + ''@fieldname'' + '' <> d.'' + ''@fieldname'' 
       + '' or (i.'' + ''@fieldname'' + '' is null and  d.''
                                + ''@fieldname''
                                + '' is not null)'' 
       + '' or (i.'' + @fieldname + '' is not null and  d.'' 
                                + ''@fieldname''
                                + '' is null)''
               EXEC (@sql)
       END
END
END
')


EXECUTE sp_executesql @sql, N'@triggertable nvarchar(max)', @triggertable = @triggertable

これが、テーブルの名前をプルして各テーブルのトリガーにデータを入力するために作成したカーソルです。

DECLARE @table varchar(100)

DECLARE trigger_cursor CURSOR FOR

SELECT TABLE_NAME 
FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_TYPE='BASE TABLE' AND TABLE_NAME NOT LIKE '%_audit'

OPEN trigger_cursor
FETCH NEXT FROM trigger_cursor INTO @table 

WHILE @@FETCH_STATUS = 0
BEGIN
EXEC [InsertTRIGGER] @triggertable = '@table'

FETCH NEXT FROM trigger_cursor INTO @table
END
CLOSE trigger_cursor
DEALLOCATE trigger_cursor

私が得ているエラーはこれです

メッセージ156、レベル15、状態1、行3キーワード「TRIGGER」の近くの構文が正しくありません。メッセージ156、レベル15、状態1、23行目キーワード「IF」の近くの構文が正しくありません。メッセージ137、レベル15、状態1、27行目スカラー変数「@TableName」を宣言する必要があります。メッセージ137、レベル15、状態1、行30スカラー変数「@UserName」を宣言する必要があります。メッセージ137、レベル15、状態1、行37

実際にはすべての変数がリストされていますが、すでにたくさん投稿しているので、目を離さないでください。

簡単なことだと思います。2つ目の目が必要です。問題は、トリガーを作成するprocの最初、または@sqlを実行する最後にあると思います。

2
user119362

トリガーの名前の一部として、およびテーブルの名前として、使用できない場所で変数を使用しようとしています。 @triggertableをパラメーターとして渡す代わりに、SQLステートメントに直接連結して、次のように置き換えることもできます。

@triggertable

と:

' + @triggertable + '

次に、必要な結果が得られるはずです。システムテーブルからテーブル名を取得していることを考えると、ここでSQLインジェクションについて実際に心配する必要はありません。カーソルがテーブル名を返す方法を再確認してください。場合によっては、次のように返されることがあります。

[tablename]

ただの代わりに:

tablename

Kenneth Fisherが指摘したように、実行する代わりに@sqlを印刷して、目的のように見えることを確認してください(sp_executesqlに渡したパラメーターを削除したため)。

1
RDFozz