0

挿入されたテーブルと削除されたテーブルを使用する際の 1 つの注意点は、両方とも空になる可能性があることです。他に知っておくべきキャッチャーはありますか?たとえば、挿入されたテーブルには、新しいレコードと更新されたレコードを含めることができますか?

トリガー内のアクションを検出するために、このロジックに依存しています。

IF EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'U'
IF EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'I'
IF NOT EXISTS(SELECT * FROM inserted) AND EXISTS(SELECT * FROM deleted) SET @operation = 'D'
IF NOT EXISTS(SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted) SET @operation = 'X'

編集:

これが、監査証跡の問題に対する私の解決策です。挿入、偽の更新、実際の更新、および削除を行う 1 つの MERGE ステートメントでテストされています。

ALTER TRIGGER [dbo].[LogInsertEditDelete]
ON [dbo].[<TableToAudit>]
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

--You will need to change @table to match the table to be audited
DECLARE @table VARCHAR(50)
SELECT @table = '<TableToAudit>'

-- date and user
DECLARE @updatedBy VARCHAR(50),
        @timestamp DateTime
SELECT @updatedBy = SYSTEM_USER,
       @timestamp = GETDATE()

-- Action, U = update, I = insert, D = delete
DECLARE @insertedCount int, 
        @deletedCount int
SET @insertedCount = (SELECT COUNT(*) FROM inserted)
SET @deletedCount = (SELECT COUNT(*) FROM deleted)

-- handle no action
IF @insertedCount = 0 AND @deletedCount = 0 RETURN  

-- handle update
IF @insertedCount <> 0 AND @deletedCount <> 0
BEGIN
    INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1)
    SELECT
        'U', 
        @table,
        @timestamp,
        @updatedBy,
        CONVERT(VARCHAR(255), i.Id)
    FROM
        (SELECT Id, BINARY_CHECKSUM(*) Version FROM inserted) i
        INNER JOIN
        (SELECT Id, BINARY_CHECKSUM(*) Version FROM deleted) d
        ON i.Id = d.Id
    WHERE
        i.Version <> d.Version  

    RETURN      
END

-- handle deletes and inserts
INSERT Audit (Type, TableName, UpdateDate, UpdatedBy, PK1)
SELECT          
    CASE 
        WHEN i.Id IS NOT NULL AND d.Id IS NULL THEN 'I'
        WHEN i.Id IS NULL AND d.Id IS NOT NULL THEN 'D'
    END,
    @table,
    @timestamp,
    @updatedBy,
    CONVERT(VARCHAR(255), COALESCE(i.Id, d.Id))
FROM inserted i 
        FULL OUTER JOIN
     deleted d 
        ON i.Id = d.Id
WHERE i.Id IS NULL OR
      d.Id IS NULL

END

テーブルごとに主キーを指定する必要があるため、このソリューションは一般的なものではありません。

4

2 に答える 2

1

全4ケース可能です。特に、更新の場合は「両方」、DML ステートメントが行を変更しなかった場合は「なし」です。

EXISTSこの最後の条件は、クエリを使用するよりも安価に検出できます。

if @@ROWCOUNT = 0
    return

EXIST効率を向上させるために、各テーブルが一度だけクエリになるように -check を少し再構築できます。

于 2012-09-05T19:03:11.520 に答える
1

コメントした後、MERGE で挿入および削除されたテーブルに何が含まれるかについて興味を持ちました。次に例を示します。

create table #test (test1 int identity not null, test2 varchar(34) null)


Insert into #test (test2)
values('test'), ('test2')

select * from #test


declare @output table(test1I int, test1D int, test2I varchar (34), test2D varchar (34))


MERGE #test AS target
USING (SELECT test1, test2 from #test ) AS source (test1, test2)
ON (target.test1 = source.test1)
WHEN MATCHED AND target.test1 = 1
    THEN UPDATE SET target.test2 = 'test3'
WHEN  MATCHED 
    THEN DELETE

OUTPUT inserted.test1, deleted.test1,  inserted.test2, deleted.test2 into @output ;

select 'Inserted/deleted contents', * from @output
select * from #test

Drop table #test

挿入または更新を行っている場合、同様のことが起こります。

レコードごとに知る必要がある場合は、スカラー変数を使用するのではなく、ジョインと case ステートメントを使用して各レコードのステータスを把握することをお勧めします。トリガーは、一度に 1 つのレコードではなく、挿入/更新/削除されたレコードのセット全体に対して動作することを忘れないでください。そのため、スカラー変数の使用は、多くの場合、正しく行っていないことの手がかりになります。トリガーが何をしようとしているのかの例をもっと教えていただければ、現在のアプローチはすべてのレコードをカバーしていないように見えるため、問題の解決に役立ちます。

上記のコメントに基づいて、おそらくこれにより、トリガー内で何を試すかについていくつかのアイデアが得られます。

Insert dbo.Audittable (Id, NewField1Value, OldField1Value, ActionTaken, ActionDate, ActionUser)
select coalesce(inserted.id, deleted.id) as Id, inserted.field1 as newField1value, deleted.field1 as oldField1value ,
case when inserted.id is not null and deleted.id is not null then 'Update'
when inserted.id is not null and deleted.id is  null then 'Insert'
when inserted.id is null and deleted.id is not null then 'Delete'
End as ActionTaken
, getdate() as ActionDate
, system_user as ActionUser
from inserted
full outer join deleted on inserted.id = deleted.id

トリガーをテストするときは、少なくとも次のテスト ケースが必要です。

  • シングルレコード挿入
  • 単一レコードの削除
  • シングルレコード更新
  • 複数レコードの挿入
  • 複数レコードの削除
  • 複数のレコード更新
  • 挿入と削除の両方を行う複数レコードのマージ
  • 更新と削除の両方を行う複数レコードのマージ
  • 挿入と更新の両方を行う複数レコードのマージ

トリガーが行うすべてのことによっては、さらに必要になる場合があります。

于 2012-09-05T19:36:54.030 に答える