Wstaw Wyzwalacz aktualizacji, jak określić, czy wstawić, czy zaktualizować

162

Muszę napisać Wstawianie, aktualizację wyzwalacza w tabeli A, która usunie wszystkie wiersze z tabeli B, której jedna kolumna (powiedzmy Desc) ma wartości takie jak wartość wstawiona / zaktualizowana w kolumnie tabeli A (powiedz Col1). Jak bym go pisał, aby móc obsługiwać zarówno przypadki aktualizacji, jak i wstawiania. Jak określić, czy wyzwalacz jest wykonywany dla aktualizacji lub wstawienia.

MSIL
źródło

Odpowiedzi:

167

Wyzwalacze mają specjalne INSERTEDi DELETEDtabele do śledzenia danych „przed” i „po”. Możesz więc użyć czegoś takiego, jak IF EXISTS (SELECT * FROM DELETED)wykryć aktualizację. Masz tylko wiersze w czasie DELETEDaktualizacji, ale zawsze są wiersze INSERTED.

Poszukaj „wstawiono” w CREATE TRIGGER .

Edycja, 23 listopada 2011

Po komentarzu ta odpowiedź jest tylko dla INSERTEDi UPDATEDwyzwala.
Oczywiście wyzwalacze DELETE nie mogą mieć „zawsze wierszy w INSERTED”, jak powiedziałem powyżej

gbn
źródło
Spójrz na odpowiedź @ MikeTeeVee poniżej, aby uzyskać pełną odpowiedź. Ten jest niekompletny.
Lorenz Meyer
1
Oryginalne pytanie @LorenzMeyer tego nie potrzebuje. Mam też ISTNIEJE (SELECT * FROM DELETED). Nie wiem, dlaczego myślisz, że to nie jest kompletne ...
gbn
@LorenzMeyer może również odnosić się do stwierdzenia „ Masz tylko wiersze w DELETED podczas aktualizacji, ale zawsze są wiersze w INSERTED. Nie zawsze jest to prawdą, ponieważ zdarzają się sytuacje, gdy wywoływany jest wyzwalacz aktualizacji / wstawiania, a opcja WSTAWIONA jest pusty. W mojej odpowiedzi wyjaśniam, jak mogło to być spowodowane tym, że Predykat eliminował jakiekolwiek zmiany danych. W tym przypadku wyzwalacz jest nadal wywoływany dla próby DML, ale tabele DELETED i INSERTED są puste. Dzieje się tak, ponieważ SQL nadal uwzględnia momenty, w których chcesz rejestrować każdą próbę DML (nawet jeśli nie zmieniają one żadnych danych).
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
net_prog
źródło
1
Lubię też pisać SELECT 1 FROM INSERTED, ponieważ wydaje mi się, że sygnalizuje to wyraźniej zamiar, ale byłbym zawiedziony przez programistów MSSQL, gdyby to miało coś zmienić w tym kontekście ...
Lukáš Lánský
26
IF EXISTS (SELECT * ...) i IF EXISTS (SELECT 1) ... mają dokładnie taką samą wydajność. Wiersz w ogóle nie jest odczytywany ani pobierany. W rzeczywistości możesz również użyć JEŻELI ISTNIEJE (SELECT 1/0 ...) i nadal będzie działać i nie spowoduje błędu dzielenia przez zero.
Endrju,
1
Tworzyłem osobne wyzwalacze do wstawiania, aktualizowania i usuwania. Teraz dobrze jest wiedzieć, że można je łączyć!
UJS
2
Jeśli ktoś napisze zapytanie w celu WSTAWIANIA i USUWANIA dwóch różnych wierszy (wstawia nowy wiersz i usuwa inny wiersz w tym samym skrypcie), czy możliwe jest, że wyzwalacz skonfigurowany w powyższy sposób faktycznie zidentyfikuje to jako AKTUALIZACJĘ (nawet jeśli intencja w rzeczywistości nie jest aktualizacją), ponieważ w tabelach INSERTED / DELETED sql znajdują się dane?
mche
87

Wiele z tych sugestii nie jest branych pod uwagę, jeśli uruchomisz instrukcję usuwania, która niczego nie usuwa.
Załóżmy, że próbujesz usunąć, gdzie identyfikator jest równy jakiejś wartości, której nie ma w tabeli.
Twój wyzwalacz nadal jest wywoływany, ale w tabelach usuniętych lub wstawionych nie ma nic.

Użyj tego dla bezpieczeństwa:

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

Specjalne podziękowania dla @KenDog i @Net_Prog za ich odpowiedzi.
Zbudowałem to na podstawie ich skryptów.

MikeTeeVee
źródło
3
To jest nagroda, obsługa nieistniejących usuniętych. Dobra robota!
Andrew Wolfe,
6
Możemy również mieć AKTUALIZACJĘ, która nie wpłynęła na żadne wiersze (lub nawet na INSERT).
Razvan Socol
@AndrewWolfe? Co ty mówisz? Pytanie konkretnie stwierdza, że „Muszę napisać wstawkę, aktualizację wyzwalacza w tabeli A” . Nic o wyzwalaczach DELETE.
ypercubeᵀᴹ
@ ypercubeᵀᴹ przepraszam, około 80% moich wyzwalaczy obejmuje wszystkie trzy czasy.
Andrew Wolfe
18

Używam następujących, poprawnie wykrywam również instrukcje delete, które niczego nie usuwają:

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;
Sob
źródło
4
ten jednak nieprawidłowo wykrywa instrukcje, które nic nie wstawiają lub niczego nie aktualizują.
Roman Pekar,
11

Po wielu poszukiwaniach nie mogłem znaleźć dokładnego przykładu pojedynczego wyzwalacza SQL Server, który obsługuje wszystkie (3) trzy warunki akcji wyzwalacza INSERT, UPDATE i DELETE. W końcu znalazłem wiersz tekstu, który mówił o fakcie, że gdy nastąpi DELETE lub UPDATE, wspólna tabela DELETED będzie zawierała zapis dla tych dwóch akcji. Na podstawie tych informacji stworzyłem następnie małą procedurę Action, która określa, dlaczego wyzwalacz został aktywowany. Ten typ interfejsu jest czasami potrzebny, gdy istnieje zarówno wspólna konfiguracja, jak i określona akcja, która ma zostać wykonana na wyzwalaczu INSERT vs UPDATE. W takich przypadkach utworzenie oddzielnego wyzwalacza dla UPDATE i INSERT stałoby się problemem konserwacyjnym. (tj. czy oba wyzwalacze zostały poprawnie zaktualizowane pod kątem niezbędnej poprawki wspólnego algorytmu danych?)

W tym celu chciałbym podać następujący fragment kodu zdarzenia z wieloma wyzwalaczami do obsługi INSERT, UPDATE, DELETE w jednym wyzwalaczu dla Microsoft SQL Server.

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   
KenDog
źródło
9

Uważam, że zagnieżdżone ifs są trochę zagmatwane i:

Płaskie jest lepsze niż zagnieżdżone [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
guneysus
źródło
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
Athul Nalupurakkal
źródło
5

Spróbuj tego..

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
Cesarin
źródło
4

chociaż podoba mi się również odpowiedź opublikowana przez @Alex, oferuję tę odmianę powyższego rozwiązania @ Graham

używa to wyłącznie istnienia rekordów w tabelach INSERTED i UPDATED, w przeciwieństwie do używania COLUMNS_UPDATED dla pierwszego testu. Zapewnia również paranoiczną ulgę programistom, wiedząc, że rozważano ostateczny przypadek ...

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

otrzymasz NOOP z oświadczeniem takim jak:

update tbl1 set col1='cat' where 1=2
greg
źródło
Wygląda na to, że pierwszy ENDjest nieprawidłowo wcięty! (powodując pytanie, gdzie pierwszy BEGINjest zamknięty)
S.Serpooshan
inne if i final else zawierają pojedyncze instrukcje. początek i koniec są naprawdę niepotrzebne, ponieważ IF / Else jest pojedynczą instrukcją. Poprawiłem wcięcie. Dzięki za pomoc.
greg
3

To może być szybszy sposób:

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'
Graham
źródło
4
Ten sposób nie działa dla tabel z dużą liczbą kolumn, ponieważ column_updated () zwraca zmienną binarną, która jest ogromna. Tak więc „> 0” kończy się niepowodzeniem, ponieważ wartość 0 jest domyślnie zapisaną wewnętrznie liczbą znacznie mniejszą niż wartość zwrócona z column_updated ()
Graham,
3

Potencjalny problem z dwoma oferowanymi rozwiązaniami polega na tym, że w zależności od sposobu ich zapisu zapytanie aktualizujące może zaktualizować zero rekordów, a zapytanie wprowadzające może wstawić zero rekordów. W takich przypadkach zestawy wstawionych i usuniętych rekordów będą puste. W wielu przypadkach, jeśli zarówno wstawione, jak i usunięte zestawy rekordów są puste, możesz po prostu wyjść z wyzwalacza bez robienia czegokolwiek.

Chuck Bevitt
źródło
2

Znalazłem mały błąd w Grahams, poza tym fajnym rozwiązaniem:

Powinien być IF COLUMNS_UPDATED () < > 0 - wstaw lub zaktualizuj
zamiast> 0 prawdopodobnie dlatego, że górny bit jest interpretowany jako SIGNED bit znaku liczby całkowitej ... (?). Więc w sumie:

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
Mogens Meier Lysemose
źródło
1

To działa dla mnie:

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

Ponieważ nie wszystkie kolumny mogą być aktualizowane na raz, możesz sprawdzić, czy dana kolumna jest aktualizowana za pomocą czegoś takiego:

IF UPDATE([column_name])
KRAZY
źródło
Wyzwaniem w tym rozwiązaniu jest znajomość nazwy kolumny. Niektóre inne są zaprojektowane tak, że możesz po prostu skopiować wklej z biblioteki fragmentów. Mała kwestia, ale biorąc pod uwagę wszystko, ogólne rozwiązanie jest lepsze niż rozwiązanie dla konkretnego przypadku. MOIM ZDANIEM.
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
Alex
źródło
1
Nie użyłbym COUNT (*) ze względu na wydajność - musi przeskanować całą tabelę. Zamiast tego ustawiłbym flagę za pomocą IF EXISTS (SELECT * FROM INSERTED), to samo dla DELETED. Wiem, że normalnie dotyczy to tylko kilku wierszy, ale po co spowalniać system.
Endrju,
Miałem zamieścić coś bardzo podobnego jako rozwiązanie. Jest trochę rozwlekły, ale bardzo czytelny. Sprawiedliwy kompromis. Podoba mi się również rozwiązanie Grahms powyżej.
greg
1

Lubię rozwiązania, które są „eleganckie informatycznie”. Moje rozwiązanie tutaj trafia w pseudotabele [wstawione] i [usunięte] raz, aby uzyskać ich statusy i umieszcza wynik w zmiennej bitowej. Następnie każdą możliwą kombinację INSERT, UPDATE i DELETE można łatwo przetestować w całym wyzwalaczu za pomocą wydajnych obliczeń binarnych (z wyjątkiem mało prawdopodobnej kombinacji INSERT lub DELETE).

Zakłada, że ​​nie ma znaczenia, jaka była instrukcja DML, jeśli żadne wiersze nie zostały zmodyfikowane (co powinno zadowolić zdecydowaną większość przypadków). Więc chociaż nie jest tak kompletne jak rozwiązanie Romana Pekara, jest bardziej wydajne.

Dzięki takiemu podejściu mamy możliwość jednego wyzwalacza „FOR INSERT, UPDATE, DELETE” na tabelę, co daje nam A) pełną kontrolę nad kolejnością akcji i b) jedną implementację kodu na akcję, która ma wiele akcji. (Oczywiście każdy model wdrożenia ma swoje wady i zalety; będziesz musiał ocenić swoje systemy indywidualnie pod kątem tego, co naprawdę działa najlepiej).

Zwróć uwagę, że instrukcje „istnieje (wybierz * z« wstawione / usunięte »)” są bardzo wydajne, ponieważ nie ma dostępu do dysku ( 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
JediSQL
źródło
Dzięki za to rozwiązanie, które pasuje do mojego kontekstu. Czy poleciłbyś sposób zaktualizowania kolumny LastUpdated zaktualizowanego / wstawionego wiersza? Czy poleciłbyś również sposób przechowywania w innej tabeli identyfikatora usuniętego wiersza (może to być klucz złożony)?
Sébastien
0

Szybkie rozwiązanie MySQL

Przy okazji: używam MySQL PDO.

(1) W tabeli z automatycznym zwiększaniem wartości po prostu pobierz najwyższą wartość (nazwa mojej kolumny = id) z kolumny zwiększonej, gdy każdy skrypt zostanie uruchomiony jako pierwszy:

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

(2) Uruchom zapytanie MySQL w normalny sposób i rzutuj wynik na liczbę całkowitą, np .:

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

(3) Po zapytaniu „INSERT INTO ... ON DUPLICATE KEY UPDATE” pobierz ostatnio wstawiony identyfikator w preferowany sposób, np .:

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

(4) Porównaj i zareaguj: Jeśli lastInsertId jest wyższy niż najwyższy w tabeli, prawdopodobnie jest to INSERT, prawda? I wzajemnie.

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

Wiem, że to szybkie i może brudne. I to jest stary post. Ale, hej, szukałem rozwiązania przez długi czas, a może i tak ktoś uzna mój sposób za przydatny. Wszystkiego najlepszego!

maxpower9000
źródło
0

po prostu prosty sposób

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 
aden
źródło
Zgodnie z moim SSMS IDE, twoja składnia nie jest poprawna w sposobie pakowania logiki w bloki IF BEGIN - END ELSE BEGIN - END.
Erutan409
0

W pierwszym scenariuszu przypuszczałem, że twoja tabela ma kolumnę IDENTITY

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

W drugim scenariuszu nie trzeba używać kolumny IDENTITTY

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
Aleksandr Fedorenko
źródło
Mam ten sam problem, ktoś może mi pomóc. Zobacz poniższy 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

JEŚLI jego aktualizacja

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

jeśli jego wstawienie

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0
thejustv
źródło
0

Używam tych exists (select * from inserted/deleted)zapytań przez długi czas, ale to wciąż za mało dla pustych operacji CRUD (gdy nie ma rekordów w insertedi deletedtabelach). Po dokładnym zbadaniu tego tematu znalazłem bardziej precyzyjne rozwiązanie:

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'

Można również użyć, columns_updated() & power(2, column_id - 1) > 0aby sprawdzić, czy kolumna została zaktualizowana, ale nie jest to bezpieczne w przypadku tabel z dużą liczbą kolumn. Użyłem nieco złożonego sposobu obliczania (zobacz pomocny artykuł poniżej).

Ponadto to podejście nadal będzie niepoprawnie klasyfikować niektóre aktualizacje jako wstawienia (jeśli aktualizacja ma wpływ na każdą kolumnę w tabeli) i prawdopodobnie sklasyfikuje wstawienia, w których tylko wartości domyślne są wstawiane jako usunięte, ale są one królem rzadkich operacji (w leasingu w moim systemie są). Poza tym w tej chwili nie wiem, jak ulepszyć to rozwiązanie.

Roman Pekar
źródło
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'
Haridas Purayil
źródło
0

robię to:

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

1 -> włóż

2 -> usuń

3 -> aktualizacja

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)
elle0087
źródło
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;
David
źródło