Insérer le déclencheur de mise à jour comment déterminer si l'insertion ou la mise à jour

162

J'ai besoin d'écrire un déclencheur d'insertion, de mise à jour sur la table A qui supprimera toutes les lignes de la table B dont une colonne (par exemple Desc) a des valeurs comme la valeur insérée / mise à jour dans la colonne de la table A (par exemple Col1). Comment pourrais-je l'écrire pour pouvoir gérer à la fois les cas de mise à jour et d'insertion. Comment déterminer si le déclencheur est exécuté pour une mise à jour ou une insertion.

MSIL
la source

Réponses:

167

Les déclencheurs ont des tables spéciales INSERTEDet DELETEDpour suivre les données «avant» et «après». Vous pouvez donc utiliser quelque chose comme IF EXISTS (SELECT * FROM DELETED)pour détecter une mise à jour. Vous n'avez que des lignes dans DELETEDlors de la mise à jour, mais il y a toujours des lignes dans INSERTED.

Recherchez "inséré" dans CREATE TRIGGER .

Edit, 23 novembre 2011

Après commentaire, cette réponse est uniquement pour INSERTEDet UPDATEDdéclenche.
Évidemment, les déclencheurs DELETE ne peuvent pas avoir "toujours des lignes dans INSERTED" comme je l'ai dit ci-dessus

gbn
la source
Regardez la réponse de @ MikeTeeVee ci-dessous pour une réponse complète. Celui-ci est incomplet.
Lorenz Meyer
1
La question originale de @LorenzMeyer n'en a pas besoin. J'ai aussi EXISTS (SELECT * FROM DELETED). Je ne sais pas pourquoi vous pensez que ce n'est pas complet ...
gbn
Ce à quoi @LorenzMeyer peut également faire référence est l'instruction " Vous n'avez que des lignes dans DELETED lors de la mise à jour, mais il y a toujours des lignes dans INSERTED. " Ce n'est pas toujours vrai car il y a des moments où le déclencheur de mise à jour / insertion est appelé et INSERTED est vide. Dans ma réponse, j'explique comment cela pourrait être causé par le prédicat éliminant toutes les données de la modification. Dans ce cas, le déclencheur est toujours appelé pour la tentative DML, mais les tables DELETED et INSERTED sont vides. Cela est dû au fait que SQL tient toujours compte des moments où vous souhaitez enregistrer chaque tentative DML (même lorsqu'ils ne modifient aucune donnée).
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
la source
1
J'aime aussi écrire SELECT 1 FROM INSERTED car je pense que cela signale plus clairement l'intention, mais je serais déçu par les programmeurs MSSQL si cela fait une différence dans ce contexte ...
Lukáš Lánský
26
IF EXISTS (SELECT * ...) et IF EXISTS (SELECT 1) ... ont exactement les mêmes performances. La ligne n'est ni lue ni extraite du tout. En fait, vous pouvez également utiliser IF EXISTS (SELECT 1/0 ...) et cela fonctionnera toujours et ne causera pas d'erreur de division par zéro.
Endrju
1
Je créais des déclencheurs séparés pour l'insertion, la mise à jour et la suppression. Maintenant, c'est génial de savoir qu'ils peuvent être combinés!
UJS
2
Si quelqu'un écrit une requête pour INSÉRER et SUPPRIMER deux lignes différentes (insérer une nouvelle ligne et supprimer une autre ligne dans le même script), est-il possible que le déclencheur configuré de la manière ci-dessus l'identifie réellement comme une MISE À JOUR (même si l'intention n'est pas réellement une mise à jour) en raison de la présence de données dans les tables SQL INSERTED / DELETED?
mche
87

Beaucoup de ces suggestions ne sont pas prises en compte si vous exécutez une instruction de suppression qui ne supprime rien.
Supposons que vous essayez de supprimer où un ID équivaut à une valeur qui n'existe pas dans la table.
Votre déclencheur est toujours appelé mais il n'y a rien dans les tables supprimées ou insérées.

Utilisez ceci pour être sûr:

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

Un merci spécial à @KenDog et @Net_Prog pour leurs réponses.
J'ai construit cela à partir de leurs scripts.

MikeTeeVee
la source
3
Celui-ci est le prix, traitement supprimé inexistant. Bon travail!
Andrew Wolfe
6
Nous pouvons également avoir une UPDATE qui n'affecte aucune ligne (ou même un INSERT).
Razvan Socol
@AndrewWolfe? Que dites-vous? La question indique spécifiquement que "je dois écrire un déclencheur d'insertion, de mise à jour sur la table A" . Rien sur les déclencheurs DELETE.
ypercubeᵀᴹ
@ ypercubeᵀᴹ désolé, environ 80% de mes déclencheurs couvrent les trois horaires.
Andrew Wolfe
18

J'utilise ce qui suit, il détecte également correctement les instructions de suppression qui ne suppriment rien:

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;
Sam
la source
4
celui-ci détecte de manière incorrecte les instructions qui n'insèrent rien ou ne mettent rien à jour, cependant.
Roman Pekar
11

Après de nombreuses recherches, je n'ai pas pu trouver d'exemple exact d'un seul déclencheur SQL Server qui gère les (3) trois conditions des actions de déclenchement INSERT, UPDATE et DELETE. J'ai finalement trouvé une ligne de texte qui parlait du fait que lorsqu'un DELETE ou UPDATE se produit, la table DELETE commune contiendra un enregistrement pour ces deux actions. Sur la base de ces informations, j'ai ensuite créé une petite routine d'action qui détermine pourquoi le déclencheur a été activé. Ce type d'interface est parfois nécessaire lorsqu'il y a à la fois une configuration commune et une action spécifique à se produire sur un déclencheur INSERT vs UPDATE. Dans ces cas, créer un déclencheur distinct pour la mise à jour et l'INSERT deviendrait un problème de maintenance. (c.-à-d. les deux déclencheurs ont-ils été mis à jour correctement pour la correction d'algorithme de données commune nécessaire?)

À cette fin, je voudrais donner l'extrait de code d'événement multi-déclencheur suivant pour gérer INSERT, UPDATE, DELETE dans un déclencheur pour un serveur 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
la source
9

Je crois que les si imbriqués sont un peu déroutants et:

Le plat est mieux que niché [Le Zen de 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
la source
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
la source
5

Essaye ça..

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
Césarine
la source
4

bien que j'aime aussi la réponse publiée par @Alex, j'offre cette variante à la solution de @ Graham ci-dessus

cela utilise exclusivement l'existence d'enregistrement dans les tables INSERTED et UPDATED, par opposition à l'utilisation de COLUMNS_UPDATED pour le premier test. Il apporte également un soulagement au programmeur paranoïaque sachant que le cas final a été considéré ...

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

vous obtiendrez NOOP avec une déclaration comme celle-ci:

update tbl1 set col1='cat' where 1=2
Greg
la source
Il semble que le premier ENDsoit mal indenté! (provoquant la question de savoir où le premier BEGINest fermé)
S.Serpooshan
les else if et final else contiennent des instructions uniques. le début et la fin sont vraiment inutiles puisque IF / Else est une seule instruction. J'ai corrigé l'indentation. Merci pour l'aide.
greg
3

Cela pourrait être un moyen plus rapide:

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
la source
4
Cette méthode ne fonctionne pas pour les tables avec un grand nombre de colonnes car columns_updated () renvoie un varbinary qui est énorme. Ainsi, le "> 0" échoue car le 0 par défaut est un nombre stocké en interne beaucoup plus petit que la valeur renvoyée par columns_updated ()
Graham
3

Un problème potentiel avec les deux solutions proposées est que, selon la façon dont elles sont écrites, une requête de mise à jour peut mettre à jour zéro enregistrement et une requête d'insertion peut insérer zéro enregistrement. Dans ces cas, les jeux d'enregistrements insérés et supprimés seront vides. Dans de nombreux cas, si les jeux d'enregistrements insérés et supprimés sont vides, vous souhaiterez peut-être simplement quitter le déclencheur sans rien faire.

Chuck Bevitt
la source
2

J'ai trouvé une petite erreur dans la solution Grahams autrement cool:

Ce devrait être IF COLUMNS_UPDATED () < > 0 - insérer ou mettre
à jour au lieu de> 0 probablement parce que le bit supérieur est interprété comme le bit de signe entier SIGNÉ ... (?). Donc au total:

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
la source
1

Cela fait l'affaire pour moi:

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

Puisque toutes les colonnes ne peuvent pas être mises à jour à la fois, vous pouvez vérifier si une colonne particulière est mise à jour par quelque chose comme ceci:

IF UPDATE([column_name])
kRAZY
la source
Un défi avec cette solution est que vous devez connaître un nom de colonne. Certains des autres sont conçus de telle sorte que vous pouvez simplement copier-coller à partir d'une bibliothèque d'extraits de code. Petit point, mais tout compte fait, une solution générique est meilleure qu'une solution spécifique à un cas. A MON HUMBLE AVIS.
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
la source
1
Je n'utiliserais pas COUNT (*) pour des raisons de performances - il doit analyser toute la table. Je définirais plutôt un drapeau en utilisant IF EXISTS (SELECT * FROM INSERTED), idem pour DELETED. Je sais que normalement il n'y a que quelques lignes affectées, mais pourquoi ralentir le système.
Endrju
J'étais sur le point de publier quelque chose de très similaire comme solution. C'est un peu verbeux, mais très lisible. Un compromis équitable. J'aime aussi la solution Grahms ci-dessus.
greg
1

J'aime les solutions «élégantes en informatique». Ma solution ici frappe les pseudotables [inséré] et [supprimé] une fois chacun pour obtenir leurs statuts et place le résultat dans une variable mappée. Ensuite, chaque combinaison possible de INSERT, UPDATE et DELETE peut facilement être testée tout au long du déclencheur avec des évaluations binaires efficaces (sauf pour la combinaison improbable INSERT ou DELETE).

Il suppose que peu importe ce qu'était l'instruction DML si aucune ligne n'était modifiée (ce qui devrait satisfaire la grande majorité des cas). Ainsi, même si elle n'est pas aussi complète que la solution de Roman Pekar, elle est plus efficace.

Avec cette approche, nous avons la possibilité d'un déclencheur "FOR INSERT, UPDATE, DELETE" par table, ce qui nous donne A) un contrôle complet sur l'ordre des actions et b) une implémentation de code par action multi-action applicable. (Évidemment, chaque modèle de mise en œuvre a ses avantages et ses inconvénients; vous devrez évaluer vos systèmes individuellement pour déterminer ce qui fonctionne vraiment le mieux.)

Notez que les instructions «existe (sélectionnez * à partir de« inséré / supprimé »)» sont très efficaces car il n'y a pas d'accès disque ( 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
la source
Merci pour cette solution qui s'inscrit dans mon contexte. Recommanderiez-vous un moyen de mettre à jour la colonne LastUpdated de la ligne mise à jour / insérée? Recommanderiez-vous également un moyen de stocker sur une autre table l'ID de la ligne supprimée (peut être une clé composée)?
Sébastien
0

Solution rapide MySQL

Au fait: j'utilise MySQL PDO.

(1) Dans une table d'incrémentation automatique, obtenez simplement la valeur la plus élevée (nom de ma colonne = id) de la colonne incrémentée une fois que chaque script est exécuté en premier:

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

(2) Exécutez la requête MySQL comme vous le feriez normalement, et transtypez le résultat en entier, par exemple:

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

(3) Après la requête "INSERT INTO ... ON DUPLICATE KEY UPDATE", obtenez le dernier identifiant inséré de la manière que vous préférez, par exemple:

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

(4) Comparez et réagissez: Si le lastInsertId est supérieur au plus élevé du tableau, c'est probablement un INSERT, non? Et vice versa.

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

Je sais que c'est rapide et peut-être sale. Et c'est un vieux post. Mais, hé, je cherchais une solution depuis longtemps, et peut-être que quelqu'un trouve mon chemin quelque peu utile de toute façon. Bonne chance!

maxpower9000
la source
0

façon simple

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
la source
Selon mon IDE SSMS, votre syntaxe n'est pas correcte avec la façon dont vous encapsulez votre logique dans des blocs IF BEGIN - END ELSE BEGIN - END.
Erutan409
0

Dans le premier scénario, j'ai supposé que votre table avait une colonne 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

Dans le deuxième scénario, vous n'avez pas besoin d'utiliser la colonne 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
la source
J'ai le même problème, quelqu'un peut m'aider. Voir le lien suivant stackoverflow.com/questions/26043106/…
Ramesh S
0
DECLARE @INSERTEDCOUNT INT,
        @DELETEDCOUNT INT

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

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

SI sa mise à jour

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

si son insertion

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0
thejustv
la source
0

J'utilise ces exists (select * from inserted/deleted)requêtes depuis longtemps, mais ce n'est toujours pas suffisant pour les opérations CRUD vides (lorsqu'il n'y a pas d'enregistrements insertedet de deletedtables). Donc, après avoir étudié un peu ce sujet, j'ai trouvé une solution plus précise:

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'

Il est également possible d'utiliser columns_updated() & power(2, column_id - 1) > 0pour voir si la colonne est mise à jour, mais ce n'est pas sûr pour les tables avec un grand nombre de colonnes. J'ai utilisé une méthode de calcul un peu complexe (voir l'article utile ci-dessous).

En outre, cette approche classifiera toujours de manière incorrecte certaines mises à jour comme des insertions (si chaque colonne de la table est affectée par la mise à jour), et elle classera probablement les insertions où seules les valeurs par défaut sont insérées en tant que suppressions, mais celles-ci sont le roi des opérations rares (à bail dans mon système ils sont). A part ça, je ne sais pas comment améliorer cette solution pour le moment.

Roman Pekar
la source
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
la source
0

je fais ça:

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

1 -> insérer

2 -> supprimer

3 -> mise à jour

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