Insertar actualización desencadenante cómo determinar si insertar o actualizar


162

Necesito escribir un desencadenador Insertar, Actualizar en la tabla A que eliminará todas las filas de la tabla B cuya columna (digamos Desc) tiene valores como el valor insertado / actualizado en la columna de la tabla A (digamos Col1). ¿Cómo podría escribirlo para poder manejar los casos de actualización e inserción? ¿Cómo determinaría si el disparador se ejecuta para una actualización o inserción?

Respuestas:


167

Los disparadores tienen tablas especiales INSERTEDy DELETEDpara rastrear datos "antes" y "después". Entonces puede usar algo como IF EXISTS (SELECT * FROM DELETED)detectar una actualización. Solo tiene filas en la DELETEDactualización, pero siempre hay filas enINSERTED .

Busque "insertado" en CREATE TRIGGER .

Edición, 23 de noviembre de 2011

Después del comentario, esta respuesta es solo para INSERTEDy UPDATEDdesencadenantes.
Obviamente, los desencadenantes DELETE no pueden tener "siempre filas adentro INSERTED" como dije anteriormente


Mire la respuesta de @ MikeTeeVee a continuación para obtener una respuesta completa. Este está incompleto.
Lorenz Meyer

1
La pregunta original de @LorenzMeyer no necesita eso. También tengo EXISTS (SELECT * FROM DELETED). No estoy seguro de por qué cree que no es completo ...
GBN

Lo @LorenzMeyer también puede estar refiriéndose a la declaración " Sólo hay filas en BORRADO en la actualización, pero hay siempre filas en insertado. " Esto no es siempre cierto, porque hay momentos en que la actualización / desencadenador de inserción se llama y se inserta es vacío. En mi respuesta, explico cómo esto podría ser causado por el predicado que elimina cualquier cambio de datos. En este caso, todavía se llama al desencadenador para el intento de DML, pero las tablas DELETED e INSERTED están vacías. Esto se debe a que SQL todavía representa los momentos en que desea registrar cada intento de DML (incluso cuando no alteran ningún dato).
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
También me gusta escribir SELECT 1 FROM INSERTED, ya que creo que indica la intención más claramente, pero los programadores de MSSQL me decepcionarían si esto hace alguna diferencia en este contexto ...
Lukáš Lánský

26
SI EXISTE (SELECCIONAR * ...) y SI EXISTE (SELECCIONAR 1) ... tienen exactamente el mismo rendimiento. La fila no se lee ni se recupera en absoluto. De hecho, también puede usar IF EXISTS (SELECT 1/0 ...) y seguirá funcionando y no causará un error de división por cero.
Endrju

1
Estaba creando disparadores separados para insertar, actualizar y eliminar. ¡Ahora es genial saber que se pueden combinar!
UJS

2
Si alguien escribe una consulta para INSERTAR y ELIMINAR dos filas diferentes (inserte una nueva fila y elimine otra fila en el mismo script), es posible que el desencadenante configurado en la forma anterior lo identifique como una ACTUALIZACIÓN (aunque la intención no es realmente una actualización) debido a que hay datos en las tablas sql INSERTED / DELETED?
mche

87

Muchas de estas sugerencias no se tienen en cuenta si ejecuta una declaración de eliminación que no elimina nada.
Supongamos que intenta eliminar donde una ID equivale a algún valor que no existe en la tabla.
Todavía se llama a su desencadenador, pero no hay nada en las tablas eliminadas o insertadas.

Use esto para estar seguro:

--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 agradecimiento especial a @KenDog y @Net_Prog por sus respuestas.
Construí esto a partir de sus guiones.


3
Este es el premio, manejo de borrado inexistente. ¡Buen trabajo!
Andrew Wolfe el

66
También podemos tener una ACTUALIZACIÓN que no afectó a ninguna fila (o incluso a un INSERT).
Razvan Socol

@ AndrewWolfe, ¿qué estás diciendo? La pregunta establece específicamente que "Necesito escribir un Insertar, Actualizar Trigger en la tabla A" . Nada sobre los desencadenantes DELETE.
ypercubeᵀᴹ

@ ypercubeᵀᴹ lo siento, aproximadamente el 80% de mis disparadores cubren los tres tiempos.
Andrew Wolfe el

18

Estoy usando lo siguiente, también detecta correctamente las declaraciones de eliminación que no eliminan nada:

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;

44
Sin embargo, este detecta incorrectamente declaraciones que no insertan nada o no actualizan nada.
Roman Pekar

11

Después de mucha búsqueda, no pude encontrar un ejemplo exacto de un solo desencadenador de SQL Server que maneje las (3) tres condiciones de las acciones de activación INSERTAR, ACTUALIZAR y ELIMINAR. Finalmente encontré una línea de texto que hablaba sobre el hecho de que cuando ocurre una ELIMINACIÓN o ACTUALIZACIÓN, la tabla BORRADA común contendrá un registro para estas dos acciones. Basado en esa información, creé una pequeña rutina de Acción que determina por qué se activó el disparador. Este tipo de interfaz a veces se necesita cuando hay una configuración común y una acción específica que se produce en un disparador INSERT vs. UPDATE. En estos casos, crear un disparador separado para la ACTUALIZACIÓN y la INSERCIÓN se convertiría en un problema de mantenimiento. (es decir, ¿se actualizaron ambos disparadores correctamente para la corrección necesaria del algoritmo de datos comunes?)

Con ese fin, me gustaría dar el siguiente fragmento de código de evento de disparador múltiple para manejar INSERT, UPDATE, DELETE en un disparador para un 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   

9

Creo que anidado es un poco confuso y:

Flat es mejor que anidado [El 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

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

Prueba esto..

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

Si bien también me gusta la respuesta publicada por @Alex, ofrezco esta variación a la solución de @ Graham anterior

esto usa exclusivamente la existencia de registros en las tablas INSERTED y UPDATED, en lugar de usar COLUMNS_UPDATED para la primera prueba. También proporciona alivio al programador paranoico sabiendo que el caso final se ha considerado ...

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

obtendrá NOOP con una declaración como la siguiente:

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

Parece que el primero ENDestá sangrado incorrectamente! (haciendo que se pregunte dónde BEGINestá cerrado el primero )
S.Serpooshan

el else if y el else contienen declaraciones únicas. el comienzo y el final son realmente innecesarios ya que IF / Else es una sola declaración. Corrija la sangría. Gracias por la ayuda.
greg

3

Esta podría ser una forma más rápida:

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'

44
De esta manera no funciona para tablas con un gran número de columnas ya que columnas_actualizadas () devuelve un varbinary que es enorme. Por lo tanto, el "> 0" falla porque el valor predeterminado de 0 es un número almacenado internamente mucho más pequeño que el valor devuelto por column_updated ()
Graham

3

Un problema potencial con las dos soluciones ofrecidas es que, dependiendo de cómo estén escritas, una consulta de actualización puede actualizar registros cero y una consulta de inserción puede insertar registros cero. En estos casos, los conjuntos de registros insertados y eliminados estarán vacíos. En muchos casos, si los conjuntos de registros insertados y eliminados están vacíos, es posible que desee salir del disparador sin hacer nada.


2

Encontré un pequeño error en la solución Grahams, de lo contrario genial:

Debería ser IF COLUMNS_UPDATED () < > 0 - insertar o actualizar en
lugar de> 0 probablemente porque el bit superior se interpreta como el bit de signo entero FIRMADO ... (?). Entonces en 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

1

Esto hace el truco para mí:

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

Como no todas las columnas se pueden actualizar a la vez, puede verificar si una columna en particular se está actualizando de la siguiente manera:

IF UPDATE([column_name])

Un desafío con esta solución es que debes conocer el nombre de una columna. Algunos de los otros están diseñados de tal manera que puede copiar y pegar desde una biblioteca de fragmentos. Pequeño punto, pero considerando todas las cosas, una solución genérica es mejor que una solución específica de caso. EN MI HUMILDE OPINIÓN.
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
No usaría COUNT (*) por razones de rendimiento: necesita escanear toda la tabla. En su lugar, establecería una marca usando IF EXISTS (SELECT * FROM INSERTED), lo mismo para DELETED. Sé que normalmente solo hay un par de filas afectadas, pero ¿por qué ralentizar el sistema?
Endrju

Estaba a punto de publicar algo muy similar como solución. Es un poco prolijo, pero muy legible. Comercio justo. También me gusta la solución de Grahms anterior.
Greg

1

Me gustan las soluciones que son "elegantes en informática". Mi solución aquí golpea los pseudotables [insertado] y [eliminado] una vez cada uno para obtener sus estados y coloca el resultado en una variable mapeada de bits. Luego, cada combinación posible de INSERTAR, ACTUALIZAR y ELIMINAR se puede probar fácilmente a lo largo del disparador con evaluaciones binarias eficientes (excepto la improbable combinación INSERTAR o ELIMINAR).

Asume que no importa cuál sea la instrucción DML si no se modificaron las filas (lo que debería satisfacer la gran mayoría de los casos). Entonces, aunque no es tan completa como la solución de Roman Pekar, es más eficiente.

Con este enfoque, tenemos la posibilidad de un disparador "PARA INSERTAR, ACTUALIZAR, BORRAR" por tabla, lo que nos da A) control completo sobre el orden de acción yb) una implementación de código por acción aplicable a múltiples acciones. (Obviamente, cada modelo de implementación tiene sus pros y sus contras; deberá evaluar sus sistemas individualmente para determinar qué es lo que realmente funciona mejor).

Tenga en cuenta que las declaraciones "existe (seleccione * de« insertado / eliminado »)" son muy eficientes ya que no hay acceso al disco ( 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

Gracias por esta solución que funciona en mi contexto. ¿Recomendaría una forma de actualizar la columna LastUpdated de la fila actualizada / insertada? ¿Recomendaría también una forma de almacenar en otra tabla el ID de la fila eliminada (puede ser una clave compuesta)?
Sébastien

0

Solución rápida MySQL

Por cierto: estoy usando MySQL PDO.

(1) En una tabla de incremento automático, solo obtenga el valor más alto (mi nombre de columna = id) de la columna incrementada una vez que cada script se ejecute primero:

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

(2) Ejecute la consulta MySQL como lo haría normalmente, y convierta el resultado a entero, por ejemplo:

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

(3) Después de la consulta "INSERTAR EN ... EN LA ACTUALIZACIÓN DE LA CLAVE DUPLICADA", obtenga la última identificación insertada de la forma que prefiera, por ejemplo:

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

(4) Compare y reaccione: si el lastInsertId es más alto que el más alto en la tabla, probablemente sea un INSERT, ¿verdad? Y viceversa.

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

Sé que es rápido y tal vez sucio. Y es una publicación vieja. Pero, oye, estuve buscando una solución durante mucho tiempo, y tal vez alguien encuentre mi camino algo útil de todos modos. ¡Todo lo mejor!


0

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

De acuerdo con mi IDE de SSMS, su sintaxis no es correcta con la forma en que está ajustando su lógica en los bloques IF BEGIN - END ELSE BEGIN - END.
Erutan409

0

En el primer escenario, supuse que tu tabla tenía una columna 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

En el segundo escenario, no es necesario usar la columna IDENTIDAD

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

Tengo el mismo problema, alguien me puede ayudar. Ver el siguiente enlace 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 su actualización

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

si su inserción

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0

0

He usado esas exists (select * from inserted/deleted)consultas durante mucho tiempo, pero aún no es suficiente para las operaciones CRUD vacías (cuando no hay registros insertedni deletedtablas). Entonces, después de investigar un poco este tema, encontré una solución más precisa:

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'

También es posible usar columns_updated() & power(2, column_id - 1) > 0para ver si la columna se actualiza, pero no es seguro para tablas con gran cantidad de columnas. He usado una forma un poco compleja de calcular (ver artículo útil a continuación).

Además, este enfoque todavía clasifica incorrectamente algunas actualizaciones como inserciones (si cada columna de la tabla se ve afectada por la actualización), y probablemente clasifique las inserciones donde solo se insertan valores predeterminados como eliminaciones, pero esos son el rey de las operaciones raras (en arrendamiento en mi sistema son). Además de eso, no sé cómo mejorar esta solución en este momento.


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

hago esto:

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

1 -> insertar

2 -> borrar

3 -> actualizar

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;
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.