sql >> Database >  >> RDS >> Sqlserver

Trigger in SQL Server - Krijg het type transactie gedaan voor de controletabel

Zodra je je trigger hebt aangepast om alle drie de bewerkingen te dekken,

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

Een ander alternatief zijn drie afzonderlijke triggers, één voor elke actie.

Wees echter op uw hoede voor MERGE als u het gebruikt... Of wees erop voorbereid wanneer u overstapt naar SQL Server 2008 of hoger.

BEWERKEN

Ik denk dat je misschien op zoek bent naar een INSTEAD OF trigger in plaats daarvan (hoe ironisch). Hier is een voorbeeld. Laten we eens kijken naar een heel eenvoudige tabel met een PK-kolom en een unieke kolom:

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

En een eenvoudige logtabel om activiteit vast te leggen:

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

De volgende INSTEAD OF trigger onderschept INSERT/UPDATE/DELETE commando's, proberen om het werk dat ze zouden hebben gedaan te repliceren, en loggen of het een mislukking of succes was:

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

Laten we een paar heel eenvoudige, impliciete transactieverklaringen proberen:

-- 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

Controleer het logboek:

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

Resultaten:

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

Natuurlijk wilt u waarschijnlijk andere kolommen in de logtabel, zoals gebruiker, datum/tijd, misschien zelfs de originele verklaring. Dit was niet bedoeld als een volledig uitgebreide auditoplossing, maar als voorbeeld.

Zoals Mikael aangeeft, is dit afhankelijk van het feit dat de buitenste batch een enkele opdracht is die een impliciete transactie start. Het gedrag zal moeten worden getest als de buitenste batch een expliciete transactie met meerdere verklaringen is.

Merk ook op dat dit geen "mislukking" vastlegt in het geval dat bijvoorbeeld een UPDATE nul rijen beïnvloedt. U moet dus expliciet definiëren wat "mislukking" betekent - in sommige gevallen moet u mogelijk uw eigen foutafhandeling in de buitenste code bouwen, niet in een trigger.




  1. Fout bij het uitvoeren van migraties:sqlalchemy.exc.CompileError:Postgresql ENUM-type vereist een naam

  2. CSV kopiëren naar door Amazon RDS gehoste Postgresql-database

  3. BUITENLANDSE SLEUTEL OP DELETE RESTRICT-fout - Oracle

  4. Hoe kan ik een kolom toevoegen die op een andere kolom in dezelfde tabel wordt verhoogd?