Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Тригер в SQL Server - Вземете типа транзакция, извършена за таблица за проверка

След като поправите тригера си, за да покрие и трите операции,

IF EXISTS (SELECT 1 FROM inserted)
BEGIN
  IF EXISTS (SELECT 1 FROM deleted)
  BEGIN
    SET @action = 'UPDATE';
  END
  ELSE
  BEGIN
    SET @action = 'INSERT';
  END
ELSE
BEGIN
  SET @action = 'DELETE';
END

Друга алтернатива са три отделни тригера, по един за всяко действие.

Внимавайте обаче с MERGE, ако го използвате... Или бъдете подготвени за него, когато преминете към SQL Server 2008 или по-нов.

РЕДАКТИРАНЕ

Мисля, че това, което може да търсите, е INSTEAD OF задейства вместо това (колко иронично). Ето един пример. Нека разгледаме много проста таблица с PK колона и уникална колона:

CREATE TABLE dbo.foobar(id INT PRIMARY KEY, x CHAR(1) UNIQUE);
GO

И проста журнална таблица за улавяне на активност:

CREATE TABLE dbo.myLog
(
    foobar_id INT, 
    oldValue  XML, 
    newValue  XML, 
    [action]  CHAR(6), 
    success   BIT
);
GO

Следното INSTEAD OF тригерът ще прихване INSERT/UPDATE/DELETE команди, опитайте се да възпроизведете работата, която биха свършили, и регистрирайте дали е било неуспех или успех:

CREATE TRIGGER dbo.foobar_inst
ON dbo.foobar
INSTEAD OF INSERT, UPDATE
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @action  CHAR(6), @success BIT;

  SELECT @action  = 'DELETE', @success = 1;

  IF EXISTS (SELECT 1 FROM inserted)
  BEGIN
    IF EXISTS (SELECT 1 FROM deleted)
      SET @action = 'UPDATE';
    ELSE
      SET @action = 'INSERT';
  END

  BEGIN TRY
    IF @action = 'INSERT'
      INSERT dbo.foobar(id, x) SELECT id, x FROM inserted;

    IF @action = 'UPDATE'
      UPDATE f SET x = i.x FROM dbo.foobar AS f
        INNER JOIN inserted AS i ON f.id = i.id;

    IF @action = 'DELETE'
        DELETE f FROM dbo.foobar AS f
          INNER JOIN inserted AS i ON f.id = i.id;
  END TRY
  BEGIN CATCH
    ROLLBACK; -- key part here!

    SET @success = 0;
  END CATCH

  IF @action = 'INSERT'
    INSERT dbo.myLog SELECT i.id, NULL, 
      (SELECT * FROM inserted WHERE id = i.id FOR XML PATH),
      @action, @success FROM inserted AS i;

  IF @action = 'UPDATE'
    INSERT dbo.myLog SELECT i.id, 
      (SELECT * FROM deleted  WHERE id = i.id FOR XML PATH),
      (SELECT * FROM inserted WHERE id = i.id FOR XML PATH),
      @action, @success FROM inserted AS i;

  IF @action = 'DELETE'
    INSERT dbo.myLog SELECT d.id, 
      (SELECT * FROM deleted  WHERE id = d.id FOR XML PATH),
      NULL, @action, @success FROM deleted AS d;
END
GO

Нека опитаме някои много прости изрази за имплицитна транзакция:

-- these succeed:

INSERT dbo.foobar SELECT 1, 'x';
GO
INSERT dbo.foobar SELECT 2, 'y';
GO

-- fails with PK violation:

INSERT dbo.foobar SELECT 1, 'z';
GO

-- fails with UQ violation:

UPDATE dbo.foobar SET x = 'y' WHERE id = 1;
GO

Проверете дневника:

SELECT foobar_id, oldValue, newValue, action, success FROM dbo.myLog;

Резултати:

foobar_id oldValue                      newValue                      action success
--------- ----------------------------- ----------------------------- ------ -------
1         NULL                          <row><id>1</id><x>x</x></row> INSERT 1
2         NULL                          <row><id>2</id><x>y</x></row> INSERT 1
1         NULL                          <row><id>1</id><x>z</x></row> INSERT 0
1         <row><id>1</id><x>x</x></row> <row><id>1</id><x>y</x></row> UPDATE 0

Разбира се, вероятно искате други колони в журналната таблица, като потребител, дата/час, може би дори оригиналното изявление. Това не беше предназначено да бъде напълно изчерпателно решение за одит, а само пример.

Както посочва Микаел, това разчита на факта, че външната партида е една команда, която стартира имплицитна транзакция. Поведението ще трябва да бъде тествано, ако външният пакет е изрична транзакция с множество изрази.

Също така имайте предвид, че това не улавя "отказ" в случай, когато, да речем, АКТУАЛИЗИРАНЕ засяга нула редове. Така че трябва изрично да дефинирате какво означава "неуспех" - в някои случаи може да се наложи да изградите свое собствено обработване на неуспех във външния код, а не в тригер.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Намерете най-малкото неизползвано число в SQL Server

  2. Ескейпиращ символ в SQL Server

  3. Шифроване на низ за връзка в app.config

  4. Как да ограничите нерегистрирани/неупълномощени потребители да преглеждат уеб страници в ASP.NET

  5. SQL Server:съставен PK проблем, когато колоните имат различен тип данни