Update einfügen, um festzustellen, ob einfügen oder aktualisieren


162

Ich muss einen Insert, Update Trigger in Tabelle A schreiben, der alle Zeilen aus Tabelle B löscht, deren eine Spalte (z. B. Desc) Werte wie den Wert enthält, der in die Spalte von Tabelle A eingefügt / aktualisiert wurde (z. B. Col1). Wie würde ich es schreiben, damit ich sowohl Update- als auch Insert-Fälle behandeln kann? Wie würde ich feststellen, ob der Trigger für eine Aktualisierung oder Einfügung ausgeführt wird?

Antworten:


166

Trigger haben spezielle INSERTEDund DELETEDTabellen, um "vor" und "nach" Daten zu verfolgen. Sie können also so etwas wie IF EXISTS (SELECT * FROM DELETED)ein Update erkennen. Beim DELETEDAktualisieren sind nur Zeilen vorhanden , es sind jedoch immer Zeilen vorhanden INSERTED.

Suchen Sie in CREATE TRIGGER nach "eingefügt" .

Bearbeiten, 23. November 2011

Nach dem Kommentar ist diese Antwort nur für INSERTEDund UPDATEDlöst aus.
Offensichtlich können DELETE-Trigger nicht "immer Zeilen in INSERTED" haben, wie ich oben sagte


Eine vollständige Antwort finden Sie in der Antwort von @ MikeTeeVee unten. Dieser ist unvollständig.
Lorenz Meyer

1
@LorenzMeyer ursprüngliche Frage braucht das nicht. Ich habe auch EXISTS (SELECT * FROM DELETED). Ich
bin

Worauf sich @LorenzMeyer möglicherweise auch bezieht, ist die Aussage " Sie haben nur Zeilen in DELETED beim Update, aber es gibt immer Zeilen in INSERTED. " Dies ist nicht immer der Fall, da der Update / Insert-Trigger manchmal aufgerufen wird und INSERTED ist leer. In meiner Antwort erkläre ich, wie dies durch das Prädikat verursacht werden kann, das verhindert, dass sich Daten ändern. In diesem Fall wird der Trigger weiterhin für den DML-Versuch aufgerufen, aber die Tabellen DELETED und INSERTED sind leer. Dies liegt daran, dass SQL immer noch Zeiten berücksichtigt, in denen Sie jeden DML-Versuch protokollieren möchten (auch wenn keine Daten geändert werden).
MikeTeeVee

127
CREATE TRIGGER dbo.TableName_IUD
ON dbo.TableName
AFTER INSERT, UPDATE, DELETE
AS 
BEGIN
    SET NOCOUNT ON;

    --
    -- Check if this is an INSERT, UPDATE or DELETE Action.
    -- 
    DECLARE @action as char(1);

    SET @action = 'I'; -- Set Action to Insert by default.
    IF EXISTS(SELECT * FROM DELETED)
    BEGIN
        SET @action = 
            CASE
                WHEN EXISTS(SELECT * FROM INSERTED) THEN 'U' -- Set Action to Updated.
                ELSE 'D' -- Set Action to Deleted.       
            END
    END
    ELSE 
        IF NOT EXISTS(SELECT * FROM INSERTED) RETURN; -- Nothing updated or inserted.

    ...

    END

1
Ich schreibe auch gerne SELECT 1 FROM INSERTED, da ich denke, dass dies die Absicht deutlicher signalisiert, aber ich würde von MSSQL-Programmierern enttäuscht sein, wenn dies in diesem Zusammenhang einen Unterschied macht ...
Lukáš Lánský

26
IF EXISTS (SELECT * ...) und IF EXISTS (SELECT 1) ... haben genau die gleiche Leistung. Die Zeile wird überhaupt nicht gelesen oder abgerufen. Tatsächlich können Sie auch IF EXISTS (SELECT 1/0 ...) verwenden, und es funktioniert weiterhin und verursacht keinen Fehler beim Teilen durch Null.
Endrju

1
Ich habe separate Trigger zum Einfügen, Aktualisieren und Löschen erstellt. Jetzt ist es gut zu wissen, dass sie kombiniert werden können!
UJS

2
Wenn jemand eine Abfrage schreibt, um zwei verschiedene Zeilen einzufügen und zu löschen (neue Zeile einfügen und eine weitere Zeile im selben Skript löschen), ist es möglich, dass der oben eingerichtete Trigger dies tatsächlich als UPDATE identifiziert (obwohl dies beabsichtigt ist) ist eigentlich kein Update) aufgrund von Daten in den INSERTED / DELETED SQL-Tabellen?
Mche

87

Viele dieser Vorschläge werden nicht berücksichtigt, wenn Sie eine Löschanweisung ausführen, die nichts löscht.
Angenommen, Sie versuchen zu löschen, wenn eine ID einem Wert entspricht, der in der Tabelle nicht vorhanden ist.
Ihr Trigger wird immer noch aufgerufen, aber die gelöschten oder eingefügten Tabellen enthalten nichts.

Verwenden Sie dies, um sicher zu gehen:

--Determine if this is an INSERT,UPDATE, or DELETE Action or a "failed delete".
DECLARE @Action as char(1);
    SET @Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
                         AND EXISTS(SELECT * FROM DELETED)
                        THEN 'U'  -- Set Action to Updated.
                        WHEN EXISTS(SELECT * FROM INSERTED)
                        THEN 'I'  -- Set Action to Insert.
                        WHEN EXISTS(SELECT * FROM DELETED)
                        THEN 'D'  -- Set Action to Deleted.
                        ELSE NULL -- Skip. It may have been a "failed delete".   
                    END)

Besonderer Dank geht an @KenDog und @Net_Prog für ihre Antworten.
Ich habe das aus ihren Skripten erstellt.


3
Dies ist der Preis, der nicht existierende gelöschte behandelt. Gute Arbeit!
Andrew Wolfe

6
Möglicherweise haben wir auch ein UPDATE, das keine Zeilen (oder sogar ein INSERT) betrifft.
Razvan Socol

@ AndrewWolfe? Was sagst du? In der Frage heißt es ausdrücklich: "Ich muss einen Einfüge- und Aktualisierungsauslöser in Tabelle A schreiben . " Nichts über DELETE-Trigger.
Ypercubeᵀᴹ

@ ypercubeᵀᴹ Entschuldigung, ungefähr 80% meiner Trigger decken alle drei Timings ab.
Andrew Wolfe

18

Ich verwende Folgendes, es erkennt auch korrekt Löschanweisungen, die nichts löschen:

CREATE TRIGGER dbo.TR_TableName_TriggerName
    ON dbo.TableName
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN
    SET NOCOUNT ON;

    IF NOT EXISTS(SELECT * FROM INSERTED)
        -- DELETE
        PRINT 'DELETE';
    ELSE
    BEGIN
        IF NOT EXISTS(SELECT * FROM DELETED)
            -- INSERT
            PRINT 'INSERT';
        ELSE
            -- UPDATE
            PRINT 'UPDATE';
    END
END;

4
Dieser erkennt jedoch fälschlicherweise Anweisungen, die nichts einfügen oder nichts aktualisieren.
Roman Pekar

11

Nach langem Suchen konnte ich kein genaues Beispiel für einen einzelnen SQL Server-Trigger finden, der alle (3) drei Bedingungen der Triggeraktionen INSERT, UPDATE und DELETE behandelt. Ich habe endlich eine Textzeile gefunden, die darüber sprach, dass die gemeinsame Tabelle DELETED einen Datensatz für diese beiden Aktionen enthält, wenn ein DELETE oder UPDATE auftritt. Basierend auf diesen Informationen habe ich dann eine kleine Aktionsroutine erstellt, die bestimmt, warum der Trigger aktiviert wurde. Diese Art von Schnittstelle wird manchmal benötigt, wenn sowohl eine gemeinsame Konfiguration als auch eine bestimmte Aktion für einen INSERT vs. UPDATE-Trigger ausgeführt werden. In diesen Fällen würde das Erstellen eines separaten Triggers für UPDATE und INSERT zu einem Wartungsproblem. (dh wurden beide Trigger ordnungsgemäß aktualisiert, um den erforderlichen gemeinsamen Datenalgorithmus zu beheben?)

Zu diesem Zweck möchte ich das folgende Ereigniscode-Snippet mit mehreren Triggern für die Behandlung von INSERT, UPDATE, DELETE in einem Trigger für einen Microsoft SQL Server bereitstellen.

CREATE TRIGGER [dbo].[INSUPDDEL_MyDataTable]
ON [dbo].[MyDataTable] FOR INSERT, UPDATE, DELETE
AS 

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with caller queries SELECT statements.
-- If an update/insert/delete occurs on the main table, the number of records affected
-- should only be based on that table and not what records the triggers may/may not
-- select.
SET NOCOUNT ON;

--
-- Variables Needed for this Trigger
-- 
DECLARE @PACKLIST_ID varchar(15)
DECLARE @LINE_NO smallint
DECLARE @SHIPPED_QTY decimal(14,4)
DECLARE @CUST_ORDER_ID varchar(15)
--
-- Determine if this is an INSERT,UPDATE, or DELETE Action
-- 
DECLARE @Action as char(1)
DECLARE @Count as int
SET @Action = 'I' -- Set Action to 'I'nsert by default.
SELECT @Count = COUNT(*) FROM DELETED
if @Count > 0
    BEGIN
        SET @Action = 'D' -- Set Action to 'D'eleted.
        SELECT @Count = COUNT(*) FROM INSERTED
        IF @Count > 0
            SET @Action = 'U' -- Set Action to 'U'pdated.
    END

if @Action = 'D'
    -- This is a DELETE Record Action
    --
    BEGIN
        SELECT @PACKLIST_ID =[PACKLIST_ID]
                    ,@LINE_NO = [LINE_NO]
        FROM DELETED

        DELETE [dbo].[MyDataTable]
        WHERE [PACKLIST_ID]=@PACKLIST_ID AND [LINE_NO]=@LINE_NO
    END
 Else
    BEGIN
            --
            -- Table INSERTED is common to both the INSERT, UPDATE trigger
            --
            SELECT @PACKLIST_ID =[PACKLIST_ID]
                ,@LINE_NO = [LINE_NO]
                ,@SHIPPED_QTY =[SHIPPED_QTY]
                ,@CUST_ORDER_ID = [CUST_ORDER_ID]
            FROM INSERTED 

         if @Action = 'I'
            -- This is an Insert Record Action
            --
            BEGIN
                INSERT INTO [MyChildTable]
                    (([PACKLIST_ID]
                    ,[LINE_NO]
                    ,[STATUS]
                VALUES
                    (@PACKLIST_ID
                    ,@LINE_NO
                    ,'New Record'
                    )
            END
        else
            -- This is an Update Record Action
            --
            BEGIN
                UPDATE [MyChildTable]
                    SET [PACKLIST_ID] = @PACKLIST_ID
                          ,[LINE_NO] = @LINE_NO
                          ,[STATUS]='Update Record'
                WHERE [PACKLIST_ID]=@PACKLIST_ID AND [LINE_NO]=@LINE_NO
            END
    END   

9

Ich glaube verschachtelt wenn ein wenig verwirrend und:

Flat ist besser als verschachtelt [The Zen of Python]

;)

DROP TRIGGER IF EXISTS AFTER_MYTABLE

GO

CREATE TRIGGER dbo.AFTER_MYTABLE ON dbo.MYTABLE AFTER INSERT, UPDATE, DELETE 

AS BEGIN 

    --- FILL THE BEGIN/END SECTION FOR YOUR NEEDS.

    SET NOCOUNT ON;

    IF EXISTS(SELECT * FROM INSERTED)  AND EXISTS(SELECT * FROM DELETED) 
        BEGIN PRINT 'UPDATE' END 
    ELSE IF EXISTS(SELECT * FROM INSERTED)  AND NOT EXISTS(SELECT * FROM DELETED) 
        BEGIN PRINT 'INSERT' END 
    ELSE IF    EXISTS(SELECT * FROM DELETED) AND NOT EXISTS(SELECT * FROM INSERTED)
        BEGIN PRINT 'DELETED' END
    ELSE BEGIN PRINT 'NOTHING CHANGED'; RETURN; END  -- NOTHING

END

9
Declare @Type varchar(50)='';
IF EXISTS (SELECT * FROM inserted) and  EXISTS (SELECT * FROM deleted)
BEGIN
    SELECT @Type = 'UPDATE'
END
ELSE IF EXISTS(SELECT * FROM inserted)
BEGIN
    SELECT @Type = 'INSERT'
END
ElSE IF EXISTS(SELECT * FROM deleted)
BEGIN
    SELECT @Type = 'DELETE'
END

5

Versuche dies..

ALTER TRIGGER ImportacionesGS ON dbo.Compra 
    AFTER INSERT, UPDATE, DELETE
AS
BEGIN
  -- idCompra is PK
  DECLARE @vIdCompra_Ins INT,@vIdCompra_Del INT
  SELECT @vIdCompra_Ins=Inserted.idCompra FROM Inserted
  SELECT @vIdCompra_Del=Deleted.idCompra FROM Deleted
  IF (@vIdCompra_Ins IS NOT NULL AND @vIdCompra_Del IS NULL)  
  Begin
     -- Todo Insert
  End
  IF (@vIdCompra_Ins IS NOT NULL AND @vIdCompra_Del IS NOT NULL)
  Begin
     -- Todo Update
  End
  IF (@vIdCompra_Ins IS NULL AND @vIdCompra_Del IS NOT NULL)
  Begin
     -- Todo Delete
  End
END

4

Ich mag zwar auch die Antwort von @Alex, aber ich biete diese Variante der obigen Lösung von @ Graham an

Dies verwendet ausschließlich das Vorhandensein von Datensätzen in den Tabellen INSERTED und UPDATED, im Gegensatz zur Verwendung von COLUMNS_UPDATED für den ersten Test. Es bietet dem paranoiden Programmierer auch Erleichterung, wenn er weiß, dass der endgültige Fall in Betracht gezogen wurde ...

declare @action varchar(4)
    IF EXISTS (SELECT * FROM INSERTED)
        BEGIN
            IF EXISTS (SELECT * FROM DELETED) 
                SET @action = 'U'  -- update
            ELSE
                SET @action = 'I'  --insert
        END
    ELSE IF EXISTS (SELECT * FROM DELETED)
        SET @action = 'D'  -- delete
    else 
        set @action = 'noop' --no records affected
--print @action

Sie erhalten NOOP mit einer Aussage wie der folgenden:

update tbl1 set col1='cat' where 1=2

Es scheint, dass der erste ENDfalsch eingerückt ist! (verursacht zu fragen, wo die erste BEGINgeschlossen ist)
S.Serpooshan

Das else if und das final else enthalten einzelne Anweisungen. Anfang und Ende sind wirklich unnötig, da IF / Else eine einzelne Anweisung ist. Ich habe den Einzug korrigiert. Danke für die Hilfe.
Greg

3

Dies könnte ein schnellerer Weg sein:

DECLARE @action char(1)

IF COLUMNS_UPDATED() > 0 -- insert or update
BEGIN
    IF EXISTS (SELECT * FROM DELETED) -- update
        SET @action = 'U'
    ELSE
        SET @action = 'I'
    END
ELSE -- delete
    SET @action = 'D'

4
Diese Methode funktioniert nicht für Tabellen mit einer großen Anzahl von Spalten, da column_updated () eine riesige Varbinary zurückgibt. Die "> 0" schlägt also fehl, weil die 0 standardmäßig eine intern gespeicherte Zahl ist, die viel kleiner ist als der Wert, der von column_updated () zurückgegeben wird
Graham,

3

Ein potenzielles Problem bei den beiden angebotenen Lösungen besteht darin, dass eine Aktualisierungsabfrage je nach Schreibweise möglicherweise keine Datensätze aktualisiert und eine Einfügeabfrage möglicherweise keine Datensätze einfügt. In diesen Fällen sind die eingefügten und gelöschten Datensatzgruppen leer. In vielen Fällen, wenn sowohl die eingefügten als auch die gelöschten Datensätze leer sind, möchten Sie den Trigger möglicherweise nur beenden, ohne etwas zu tun.


2

Ich habe einen kleinen Fehler in Grahams ansonsten cooler Lösung gefunden:

Es sollte IF COLUMNS_UPDATED () < > 0 sein - Einfügen oder Aktualisieren
anstelle von> 0, wahrscheinlich weil das oberste Bit als SIGNED Integer Sign Bit interpretiert wird ... (?). Also insgesamt:

DECLARE @action CHAR(8)  
IF COLUMNS_UPDATED() <> 0 -- delete or update?
BEGIN     
  IF EXISTS (SELECT * FROM deleted) -- updated cols + old rows means action=update       
    SET @action = 'UPDATE'     
  ELSE
    SET @action = 'INSERT' -- updated columns and nothing deleted means action=insert
END 
ELSE -- delete     
BEGIN
  SET @action = 'DELETE'
END

1

Das macht den Trick für mich:

declare @action_type int;
select @action_type = case
                       when i.id is not null and d.id is     null then 1 -- insert
                       when i.id is not null and d.id is not null then 2 -- update
                       when i.id is     null and d.id is not null then 3 -- delete
                     end
  from      inserted i
  full join deleted  d on d.id = i.id

Da nicht alle Spalten gleichzeitig aktualisiert werden können, können Sie überprüfen, ob eine bestimmte Spalte folgendermaßen aktualisiert wird:

IF UPDATE([column_name])

Eine Herausforderung bei dieser Lösung besteht darin, dass Sie einen Spaltennamen kennen müssen. Einige der anderen sind so konzipiert, dass Sie sie einfach aus einer Snippet-Bibliothek kopieren und einfügen können. Kleiner Punkt, aber alles in allem ist eine generische Lösung besser als eine fallspezifische Lösung. MEINER BESCHEIDENEN MEINUNG NACH.
Greg

1
declare @insCount int
declare @delCount int
declare @action char(1)

select @insCount = count(*) from INSERTED
select @delCount = count(*) from DELETED

    if(@insCount > 0 or @delCount > 0)--if something was actually affected, otherwise do nothing
    Begin
        if(@insCount = @delCount)
            set @action = 'U'--is update
        else if(@insCount > 0)
            set @action = 'I' --is insert
        else
            set @action = 'D' --is delete

        --do stuff here
    End

1
Ich würde COUNT (*) aus Leistungsgründen nicht verwenden - es muss die gesamte Tabelle gescannt werden. Ich würde stattdessen ein Flag setzen, indem ich IF EXISTS (SELECT * FROM INSERTED) verwende, dasselbe gilt für DELETED. Ich weiß, dass normalerweise nur ein paar Zeilen betroffen sind, aber warum das System verlangsamen.
Endrju

Ich wollte gerade etwas sehr Ähnliches als Lösung posten. Es ist ein bisschen wortreich, aber sehr lesbar. Fairer Kompromiss. Ich mag auch Grahms Lösung oben.
Greg

1

Ich mag Lösungen, die "Informatik elegant" sind. Meine Lösung hier trifft die Pseudotabellen [eingefügt] und [gelöscht] jeweils einmal, um ihren Status zu erhalten, und fügt das Ergebnis in eine Bitmap-Variable ein. Dann kann jede mögliche Kombination von INSERT, UPDATE und DELETE während des gesamten Triggers mit effizienten binären Auswertungen leicht getestet werden (mit Ausnahme der unwahrscheinlichen Kombination INSERT oder DELETE).

Es wird davon ausgegangen, dass es keine Rolle spielt, wie die DML-Anweisung lautete, wenn keine Zeilen geändert wurden (was die überwiegende Mehrheit der Fälle erfüllen sollte). Es ist zwar nicht so vollständig wie die Lösung von Roman Pekar, aber effizienter.

Mit diesem Ansatz haben wir die Möglichkeit, einen "FOR INSERT, UPDATE, DELETE" -Trigger pro Tabelle zu verwenden, wodurch wir A) die vollständige Kontrolle über die Aktionsreihenfolge und b) eine Codeimplementierung pro für mehrere Aktionen anwendbarer Aktion erhalten. (Natürlich hat jedes Implementierungsmodell seine Vor- und Nachteile. Sie müssen Ihre Systeme individuell bewerten, um herauszufinden, was wirklich am besten funktioniert.)

Beachten Sie, dass die Anweisungen "existiert (wählen Sie * aus" eingefügt / gelöscht ")" sehr effizient sind, da kein Festplattenzugriff besteht ( https://social.msdn.microsoft.com/Forums/en-US/01744422-23fe-42f6) -9ab0-a255cdf2904a ).

use tempdb
;
create table dbo.TrigAction (asdf int)
;
GO
create trigger dbo.TrigActionTrig
on dbo.TrigAction
for INSERT, UPDATE, DELETE
as
declare @Action tinyint
;
-- Create bit map in @Action using bitwise OR "|"
set @Action = (-- 1: INSERT, 2: DELETE, 3: UPDATE, 0: No Rows Modified 
  (select case when exists (select * from inserted) then 1 else 0 end)
| (select case when exists (select * from deleted ) then 2 else 0 end))
;
-- 21 <- Binary bit values
-- 00 -> No Rows Modified
-- 01 -> INSERT -- INSERT and UPDATE have the 1 bit set
-- 11 -> UPDATE <
-- 10 -> DELETE -- DELETE and UPDATE have the 2 bit set

raiserror(N'@Action = %d', 10, 1, @Action) with nowait
;
if (@Action = 0) raiserror(N'No Data Modified.', 10, 1) with nowait
;
-- do things for INSERT only
if (@Action = 1) raiserror(N'Only for INSERT.', 10, 1) with nowait
;
-- do things for UPDATE only
if (@Action = 3) raiserror(N'Only for UPDATE.', 10, 1) with nowait
;
-- do things for DELETE only
if (@Action = 2) raiserror(N'Only for DELETE.', 10, 1) with nowait
;
-- do things for INSERT or UPDATE
if (@Action & 1 = 1) raiserror(N'For INSERT or UPDATE.', 10, 1) with nowait
;
-- do things for UPDATE or DELETE
if (@Action & 2 = 2) raiserror(N'For UPDATE or DELETE.', 10, 1) with nowait
;
-- do things for INSERT or DELETE (unlikely)
if (@Action in (1,2)) raiserror(N'For INSERT or DELETE.', 10, 1) with nowait
-- if already "return" on @Action = 0, then use @Action < 3 for INSERT or DELETE
;
GO

set nocount on;

raiserror(N'
INSERT 0...', 10, 1) with nowait;
insert dbo.TrigAction (asdf) select top 0 object_id from sys.objects;

raiserror(N'
INSERT 3...', 10, 1) with nowait;
insert dbo.TrigAction (asdf) select top 3 object_id from sys.objects;

raiserror(N'
UPDATE 0...', 10, 1) with nowait;
update t set asdf = asdf /1 from dbo.TrigAction t where asdf <> asdf;

raiserror(N'
UPDATE 3...', 10, 1) with nowait;
update t set asdf = asdf /1 from dbo.TrigAction t;

raiserror(N'
DELETE 0...', 10, 1) with nowait;
delete t from dbo.TrigAction t where asdf < 0;

raiserror(N'
DELETE 3...', 10, 1) with nowait;
delete t from dbo.TrigAction t;
GO

drop table dbo.TrigAction
;
GO

Vielen Dank für diese Lösung, die in meinem Kontext Fuß fasst. Würden Sie eine Möglichkeit empfehlen, die LastUpdated-Spalte der aktualisierten / eingefügten Zeile zu aktualisieren? Würden Sie auch eine Möglichkeit empfehlen, die ID der gelöschten Zeile (möglicherweise zusammengesetzter Schlüssel) in einer anderen Tabelle zu speichern?
Sébastien

0

Schnelle Lösung MySQL

Übrigens: Ich benutze MySQL PDO.

(1) In einer Tabelle mit automatischem Inkrementieren erhalten Sie einfach den höchsten Wert (mein Spaltenname = ID) aus der inkrementierten Spalte, sobald jedes Skript zuerst ausgeführt wird:

$select = "
    SELECT  MAX(id) AS maxid
    FROM    [tablename]
    LIMIT   1
";

(2) Führen Sie die MySQL-Abfrage wie gewohnt aus und wandeln Sie das Ergebnis in eine Ganzzahl um, z.

$iMaxId = (int) $result[0]->maxid;

(3) Nach der Abfrage "INSERT IN ... ON DUPLICATE KEY UPDATE" erhalten Sie die zuletzt eingefügte ID auf Ihre bevorzugte Weise, z.

$iLastInsertId = (int) $db->lastInsertId();

(4) Vergleichen und reagieren: Wenn die lastInsertId höher als die höchste in der Tabelle ist, handelt es sich wahrscheinlich um eine INSERT, oder? Und umgekehrt.

if ($iLastInsertId > $iMaxObjektId) {
    // IT'S AN INSERT
}
else {
    // IT'S AN UPDATE
}

Ich weiß, dass es schnell und vielleicht schmutzig ist. Und es ist ein alter Beitrag. Aber hey, ich habe lange nach einer Lösung gesucht, und vielleicht findet jemand meinen Weg trotzdem etwas nützlich. Alles Gute!


0

einfach so

CREATE TRIGGER [dbo].[WO_EXECUTION_TRIU_RECORD] ON [dbo].[WO_EXECUTION]
WITH EXECUTE AS CALLER
FOR INSERT, UPDATE
AS
BEGIN  

  select @vars = [column] from inserted 
  IF UPDATE([column]) BEGIN
    -- do update action base on @vars 
  END ELSE BEGIN
    -- do insert action base on @vars 
  END

END 

Laut meiner SSMS-IDE stimmt Ihre Syntax nicht damit überein, wie Sie Ihre Logik in IF BEGIN - END ELSE BEGIN - END-Blöcke einschließen.
Erutan409

0

Im ersten Szenario habe ich angenommen, dass Ihre Tabelle die Spalte IDENTITY hat

CREATE TRIGGER [dbo].[insupddel_yourTable] ON [yourTable]
FOR INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE @action nvarchar(10)
SELECT @action = CASE WHEN COUNT(i.Id) > COUNT(d.Id) THEN 'inserted'
                      WHEN COUNT(i.Id) < COUNT(d.Id) THEN 'deleted' ELSE 'updated' END
FROM inserted i FULL JOIN deleted d ON i.Id = d.Id

Im zweiten Szenario muss die Spalte IDENTITTY nicht verwendet werden

CREATE TRIGGER [dbo].[insupddel_yourTable] ON [yourTable]
FOR INSERT, UPDATE, DELETE
AS
IF @@ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE @action nvarchar(10),
        @insCount int = (SELECT COUNT(*) FROM inserted),
        @delCount int = (SELECT COUNT(*) FROM deleted)
SELECT @action = CASE WHEN @insCount > @delCount THEN 'inserted'
                      WHEN @insCount < @delCount THEN 'deleted' ELSE 'updated' END

Ich habe das gleiche Problem, jemand kann mir helfen. Siehe den folgenden Link stackoverflow.com/questions/26043106/…
Ramesh S

0
DECLARE @INSERTEDCOUNT INT,
        @DELETEDCOUNT INT

SELECT @INSERTEDCOUNT = COUNT([YourColumnName]) FROM inserted

SELECT @DELETEDCOUNT = COUNT([YourColumnName]) FROM deleted

WENN seine Aktualisierung

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

wenn seine Einfügung

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0

0

Ich habe diese exists (select * from inserted/deleted)Abfragen schon lange verwendet, aber es reicht immer noch nicht für leere CRUD-Operationen (wenn keine Datensätze insertedund deletedTabellen vorhanden sind). Nachdem ich mich ein wenig mit diesem Thema befasst habe, habe ich eine genauere Lösung gefunden:

declare
    @columns_count int = ?? -- number of columns in the table,
    @columns_updated_count int = 0

-- this is kind of long way to get number of actually updated columns
-- from columns_updated() mask, it's better to create helper table
-- or at least function in the real system
with cte_columns as (
    select @columns_count as n
    union all
    select n - 1 from cte_columns where n > 1
), cte_bitmasks as (
    select
        n,
        (n - 1) / 8 + 1 as byte_number,
        power(2, (n - 1) % 8) as bit_mask
    from cte_columns
)
select
    @columns_updated_count = count(*)
from cte_bitmasks as c
where
    convert(varbinary(1), substring(@columns_updated_mask, c.byte_number, 1)) & c.bit_mask > 0

-- actual check
if exists (select * from inserted)
    if exists (select * from deleted)
        select @operation = 'U'
    else
        select @operation = 'I'
else if exists (select * from deleted)
    select @operation = 'D'
else if @columns_updated_count = @columns_count
    select @operation = 'I'
else if @columns_updated_count > 0
    select @operation = 'U'
else
    select @operation = 'D'

Es ist auch möglich zu columns_updated() & power(2, column_id - 1) > 0sehen, ob die Spalte aktualisiert wird, aber es ist nicht sicher für Tabellen mit einer großen Anzahl von Spalten. Ich habe eine etwas komplexe Berechnungsmethode verwendet (siehe hilfreichen Artikel unten).

Außerdem klassifiziert dieser Ansatz einige Aktualisierungen immer noch fälschlicherweise als Einfügungen (wenn jede Spalte in der Tabelle von Aktualisierungen betroffen ist), und klassifiziert wahrscheinlich Einfügungen, bei denen nur Standardwerte als Löschungen eingefügt werden, diese jedoch König seltener Operationen sind (at Leasing in meinem System sind sie). Außerdem weiß ich derzeit nicht, wie ich diese Lösung verbessern kann.


0
declare @result as smallint
declare @delete as smallint = 2
declare @insert as smallint = 4
declare @update as smallint = 6
SELECT @result = POWER(2*(SELECT count(*) from deleted),1) + POWER(2*(SELECT 
     count(*) from inserted),2)

if (@result & @update = @update) 
BEGIN
  print 'update'
  SET @result=0
END
if (@result & @delete = @delete)
  print 'delete'
if (@result & @insert = @insert)
  print 'insert'

0

Ich mache das:

select isnull((select top 1 1 from inserted t1),0) + isnull((select top 1 2 from deleted t1),0)

1 -> einfügen

2 -> löschen

3 -> aktualisieren

set @i = isnull((select top 1 1 from inserted t1),0) + isnull((select top 1 2 from deleted t1),0)
--select @i

declare @action varchar(1) = case @i when 1 then 'I' when 2 then 'D' when 3 then 'U' end
--select @action


select @action c1,* from inserted t1 where @i in (1,3) union all
select @action c1,* from deleted t1 where @i in (2)

0
DECLARE @ActionType CHAR(6);
SELECT  @ActionType = COALESCE(CASE WHEN EXISTS(SELECT * FROM INSERTED)
                                     AND EXISTS(SELECT * FROM DELETED)  THEN 'UPDATE' END,
                               CASE WHEN EXISTS(SELECT * FROM DELETED)  THEN 'DELETE' END,
                               CASE WHEN EXISTS(SELECT * FROM INSERTED) THEN 'INSERT' END);
PRINT   @ActionType;
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.