Каков правильный синтаксис для создания триггера базы данных для вставки, изменения и удаления - PullRequest
3 голосов
/ 06 января 2010

У меня есть то, что кажется базовым сценарием для триггера БД в SQL-сервере, и я столкнулся с проблемой.

У меня есть таблица Пользователи (идентификатор, имя, телефон и т. Д.), И у меня есть таблицы UsersHistory (идентификатор, действие user_id, поля, отметка времени)

Мне нужен триггер базы данных, в который в любое время вставляются, обновляются или удаляются пользователи, я хочу создать новую запись в UsersHistory с идентификатором пользователя и выполненным действием (вставить новые, обновленные поля, удаленный идентификатор. По сути, журнал аудита таблица.

это то, как далеко я прошел, но я не могу понять, как:

  1. Получить идентификатор для изменения и удаления, а также
  2. Как получить список полей, которые были изменены, и действия, которые были совершены (вставить, удалить, обновить)

CREATE TRIGGER Update_Users_History 
   ON  Users
   AFTER INSERT,DELETE,UPDATE
 AS 
 BEGIN
-- Insert statements for trigger here

insert into UsersHistory (user_id, [action], [fields], timestamp)
select  max(id) as user_id, {action ??},{fields??}  getdate() from Users)

END
GO

есть предложения?

Ответы [ 2 ]

10 голосов
/ 06 января 2010

Проще всего просто создать три триггера - по одному для каждой операции:

CREATE TRIGGER trgUserInsert
ON dbo.User AFTER INSERT
AS BEGIN
   INSERT INTO dbo.UserHistory............
END     

CREATE TRIGGER trgUserDelete
ON dbo.User AFTER DELETE
AS BEGIN
   INSERT INTO dbo.UserHistory............
END 

CREATE TRIGGER trgUserUpdate
ON dbo.User AFTER UPDATE
AS BEGIN
   INSERT INTO dbo.UserHistory............
END 

Таким образом, все просто, и вы легко понимаете, что делаете, плюс это дает вам возможность отключить триггер для одной операции, если вы, например, Нужно вставить или удалить огромный список предметов.

Внутри триггера у вас есть две "псевдотаблицы" - Inserted (для INSERT и UPDATE) и Deleted (для UPDATE и DELETE). Эти псевдотаблицы содержат значения для вновь вставленных значений (или обновленных значений в UPDATE), или тех, которые были удалены (для DELETE) или были обновлены (старые значения до обновления для операции UPDATE).

Вы должны знать, что триггер будет вызываться один раз, даже если вы обновите огромное количество строк, например, Inserted и Deleted обычно содержат несколько строк.

В качестве примера вы могли бы написать триггер «ПОСЛЕ ВСТАВКИ», как этот (просто угадав, какой может быть структура вашей таблицы ....):

CREATE TRIGGER trgUserInsert
ON dbo.User AFTER INSERT
AS BEGIN
   INSERT INTO 
      dbo.UserHistory(UserID, Action, DateTimeStamp, AuditMessage)
      SELECT 
         i.UserID, 'INSERT', getdate(), 'User inserted into table'
      FROM
         Inserted i
END     

Вы ищете способ узнать, какое "действие" вызвало этот триггер? Я не вижу никакого способа сделать это - еще одна причина держать три триггера отдельно. Единственный способ выяснить это - подсчитать строки в таблицах Inserted и Updated:

  • если оба значения больше нуля, это UPDATE
  • если в таблице Inserted есть строки, а в Deleted нет, то это INSERT
  • если в таблице Inserted нет строк, а в Deleted - DELETE

Вы также ищете «список полей, которые были обновлены» - опять же, у вас просто не будет простого решения. Вы можете просто просмотреть интересующие вас поля в таблице «Пользователи» и проверить

IF UPDATE(fieldname) ......

но это становится немного утомительным.

Или вы можете использовать функцию COLUMNS_UPDATED() - это, однако, не дает вам хороший список имен столбцов, но VARBINARY, в котором каждый столбец в основном один бит, и если он включен , этот столбец был обновлен. Не очень прост в использовании .....


Если вы действительно хотите создать один большой триггер, это может послужить основой - он определяет, какая операция вызвала срабатывание триггера, и вставит записи в вашу таблицу User_History:

CREATE TRIGGER trgUser_Universal
ON dbo.Users
AFTER INSERT, UPDATE, DELETE
AS BEGIN
  DECLARE @InsHasRows BIT = 0
  DECLARE @DelHasRows BIT = 0

  IF EXISTS(SELECT TOP 1 * FROM INSERTED)
    SET @InsHasRows = 1

  IF EXISTS(SELECT TOP 1 * FROM DELETED)
    SET @DelHasRows = 1

  DECLARE @TriggerAction VARCHAR(20)

  IF @InsHasRows = 1 AND @DelHasRows = 1 
     SET @TriggerAction = 'UPDATE'
  ELSE
     IF @InsHasRows = 1
        SET @TriggerAction = 'INSERT'
     ELSE  
        SET @TriggerAction = 'DELETE'

  IF @InsHasRows = 1
    INSERT INTO dbo.UsersHistory(user_id, [action], [fields], timestamp)
      SELECT i.UserId, @TriggerAction, null, getdate()
      FROM INSERTED i
  ELSE  
    INSERT INTO dbo.UsersHistory(user_id, [action], [fields], timestamp)
      SELECT d.UserId, @TriggerAction, null, getdate()
      FROM DELETED d
END

Я пока не включил выяснение того, какие поля были обновлены, - это оставлено читателю в качестве упражнения: -)


Помогает ли это вообще?

2 голосов
/ 06 января 2010

В триггере используются две "таблицы". Один - DELETED, а другой - INSERTED. Когда вы удаляете строку, эта строка записывается в таблицу DELETED. Когда вы вставляете строку, эта строка записывается в таблицу INSERTED. При обновлении строки старая строка находится в таблице DELETED, а новая строка - в таблице INSERTED. Таблицы DELETED и INSERTED имеют ту же схему, что и таблица, к которой вы добавляете триггер.

Вы можете проверить это решение , которое создаст для вас запрос, который выполнит все необходимые триггеры аудита, а также таблицу, в которой будут храниться аудиты, исключая любые выбранные таблицы. Он будет выполнять только триггеры UPDATE, но его можно легко изменить, чтобы сделать триггеры INSERT и DELETE.

...