Gatilho Inserir atualização como determinar se inserir ou atualizar


162

Preciso escrever um Insert, Update Trigger na tabela A, que excluirá todas as linhas da tabela B, cuja coluna (digamos Desc) possui valores como o valor inserido / atualizado na coluna da tabela A (digamos Col1). Como eu iria escrevê-lo para que eu possa lidar com os casos Update e Insert. Como eu determinaria se o gatilho é executado para uma atualização ou inserção.

Respostas:


167

Os gatilhos possuem tabelas INSERTEDe especiais DELETEDpara rastrear dados "antes" e "depois". Portanto, você pode usar algo como IF EXISTS (SELECT * FROM DELETED)para detectar uma atualização. Você só tem linhas na DELETEDatualização, mas sempre há linhas nasINSERTED .

Procure por "inserido" em CREATE TRIGGER .

Edit, 23 Nov 2011

Após o comentário, esta resposta é apenas para INSERTEDe UPDATEDdispara.
Obviamente, os gatilhos DELETE não podem ter "sempre linhas INSERTED" como eu disse acima


Veja a resposta de @ MikeTeeVee abaixo para obter uma resposta completa. Este está incompleto.
Lorenz Meyer

1
A pergunta original @LorenzMeyer não precisa disso. Eu também tenho EXISTS (SELECT * FROM DELETED). Não sei por que você acha que não está completo ...
gbn

O que @LorenzMeyer também pode estar se referindo é a declaração " Você só tem linhas em DELETED na atualização, mas sempre há linhas em INSERTED. " Isso nem sempre é verdade porque há momentos em que o gatilho Atualizar / Inserir é chamado e INSERTED é vazio. Na minha resposta, explico como isso pode ser causado pelo Predicado, eliminando a alteração de quaisquer dados. Nesse caso, o gatilho ainda é chamado para a tentativa de DML, mas as tabelas DELETED e INSERTED estão vazias. Isso ocorre porque o SQL ainda é responsável pelos momentos em que você deseja registrar cada tentativa de DML (mesmo quando eles não alteram nenhum dado).
MikeTeeVee 24/01

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
Também gosto de escrever SELECT 1 FROM INSERTED, pois acho que sinaliza intenção com mais clareza, mas ficaria desapontado com os programadores do MSSQL se isso fizer alguma diferença nesse contexto ...
Lukáš Lánský

26
SE EXISTE (SELECIONE * ...) e SE EXISTE (SELECIONE 1) ... têm exatamente o mesmo desempenho. A linha não é lida nem buscada. Na verdade, você também pode usar IF EXISTS (SELECT 1/0 ...) e ainda funcionará e não causará erro de divisão por zero.
Endrju 5/10

1
Eu estava criando Triggers separados para inserir, atualizar e excluir. Agora é ótimo saber que eles podem ser combinados!
UJS

2
Se alguém escrever uma consulta para INSERT e DELETE duas linhas diferentes (inserir nova linha e excluir outra linha no mesmo script), é possível que o gatilho configurado da maneira acima na verdade identifique isso como um UPDATE (mesmo que a intenção não é realmente uma atualização) devido à existência de dados nas tabelas sql INSERTED / DELETED?
22618 mche

87

Muitas dessas sugestões não são levadas em consideração se você executar uma instrução de exclusão que não exclui nada.
Digamos que você tente excluir onde um ID é igual a algum valor que não existe na tabela.
Seu gatilho ainda é chamado, mas não há nada nas tabelas Excluídas ou Inseridas.

Use isto para ser 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)

Agradecimentos especiais a @KenDog e @Net_Prog por suas respostas.
Eu construí isso a partir de seus scripts.


3
Este é o prêmio, tratando excluídos inexistentes. Bom trabalho!
Andrew Wolfe

6
Também podemos ter um UPDATE que não afetou nenhuma linha (ou mesmo um INSERT).
Razvan Socol

@AndrewWolfe? O que você está dizendo? A pergunta afirma especificamente que "eu preciso escrever um Insert, Update Trigger na tabela A" . Nada sobre os gatilhos DELETE.
precisa saber é o seguinte

@ ypercubeᵀᴹ desculpe, cerca de 80% dos meus gatilhos cobrem todos os três tempos.
Andrew Wolfe

18

Estou usando o seguinte, ele também detecta corretamente instruções de exclusão que não excluem 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;

4
este detecta incorretamente instruções que não inserem nada ou atualizam nada.
Roman Pekar

11

Após muita pesquisa, não consegui encontrar um exemplo exato de um único gatilho do SQL Server que lida com todas as três condições das ações de gatilho INSERT, UPDATE e DELETE. Finalmente encontrei uma linha de texto que falava sobre o fato de que, quando um DELETE ou UPDATE ocorre, a tabela DELETED comum conterá um registro para essas duas ações. Com base nessas informações, criei uma pequena rotina de ação que determina por que o gatilho foi ativado. Às vezes, esse tipo de interface é necessário quando há uma configuração comum e uma ação específica a ocorrer em um acionador INSERT vs. UPDATE. Nesses casos, criar um gatilho separado para o UPDATE e o INSERT se tornaria um problema de manutenção. (ou seja, os dois gatilhos foram atualizados corretamente para a correção necessária do algoritmo de dados comuns?)

Para esse fim, gostaria de fornecer o seguinte snippet de código de evento com vários gatilhos para manipular INSERT, UPDATE, DELETE em um gatilho para um 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

Creio aninhado ifs um pouco confuso e:

Flat é melhor que aninhado [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

Tente isso ..

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

Embora eu também goste da resposta postada por @Alex, ofereço essa variação à solução de @ Graham acima

isso usa exclusivamente a existência de registro nas tabelas INSERTED e UPDATED, em vez de usar COLUMNS_UPDATED no primeiro teste. Ele também fornece alívio paranóico ao programador, sabendo que o caso final foi 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

você receberá NOOP com uma declaração como a seguinte:

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

Parece que o primeiro ENDé recuado incorretamente! (causando a questão, onde a primeira BEGINestá fechado)
S.Serpooshan

o else if e final else contêm instruções únicas. o início e o fim são realmente desnecessários, pois o IF / Else é uma única declaração. Corrigi o recuo. Obrigado pela ajuda.
greg

3

Esta pode ser uma maneira mais 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'

4
Dessa forma, não funciona para tabelas com grande número de colunas, pois column_updated () retorna um varbinário que é enorme. Assim, a "> 0" falha porque a 0 padrão é um número armazenado internamente muito menores do que o valor retornado de COLUMNS_UPDATED ()
Graham

3

Um possível problema com as duas soluções oferecidas é que, dependendo de como elas são gravadas, uma consulta de atualização pode atualizar zero registros e uma consulta de inserção pode inserir zero registros. Nesses casos, os conjuntos de registros inseridos e excluídos estarão vazios. Em muitos casos, se os conjuntos de registros Inserido e Excluído estiverem vazios, convém sair do gatilho sem fazer nada.


2

Eu encontrei um pequeno erro na solução Grahams caso contrário legal:

Deve ser IF COLUMNS_UPDATED () < > 0 - inserir ou atualizar em
vez de> 0 provavelmente porque o bit superior é interpretado como bit de sinal inteiro ASSINADO ... (?). Então, no 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

Isso faz o truque para mim:

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 nem todas as colunas podem ser atualizadas ao mesmo tempo, você pode verificar se uma coluna específica está sendo atualizada por algo assim:

IF UPDATE([column_name])

Um desafio com esta solução é que você precisa saber o nome de uma coluna. Alguns dos outros foram projetados para que você possa copiar e colar de uma biblioteca de trechos. Ponto pequeno, mas considerando tudo, uma solução genérica é melhor que uma solução específica de caso. NA MINHA HUMILDE OPINIÃO.
585 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
Eu não usaria COUNT (*) por motivos de desempenho - ele precisa verificar a tabela inteira. Em vez disso, eu definiria um sinalizador usando IF EXISTS (SELECT * FROM INSERTED), o mesmo para DELETED. Eu sei que normalmente existem apenas algumas linhas afetadas, mas por que desacelerar o sistema.
Endrju

Eu estava prestes a postar algo muito semelhante como solução. É um pouco prolixo, mas muito legível. Comércio justo. Eu também gosto da solução Grahms acima.
585 greg

1

Gosto de soluções que são "elegantes em ciência da computação". Minha solução aqui atinge as pseudotables [inseridas] e [excluídas] uma vez para obter seus status e coloca o resultado em uma variável mapeada em bits. Em seguida, cada combinação possível de INSERT, UPDATE e DELETE pode ser prontamente testada em todo o gatilho com avaliações binárias eficientes (exceto para a improvável combinação INSERT ou DELETE).

Supõe-se que não importa qual era a instrução DML se nenhuma linha foi modificada (o que deve satisfazer a grande maioria dos casos). Portanto, embora não seja tão completa quanto a solução de Roman Pekar, é mais eficiente.

Com essa abordagem, temos a possibilidade de um gatilho "FOR INSERT, UPDATE, DELETE" por tabela, fornecendo A) controle completo sobre a ordem das ações eb) implementação de código por ação aplicável a várias ações. (Obviamente, todo modelo de implementação tem seus prós e contras; você precisará avaliar seus sistemas individualmente quanto ao que realmente funciona melhor.)

Observe que as instruções "existe (selecione * de« inserido / excluído »)" são muito eficientes, pois não há acesso ao 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

Obrigado por esta solução que aparece no meu contexto. Você recomendaria uma maneira de atualizar a coluna LastUpdated da linha atualizada / inserida? Você também recomendaria uma maneira de armazenar em outra tabela o ID da linha excluída (pode ser uma chave composta)?
Sébastien

0

Solução rápida MySQL

A propósito: estou usando o MySQL PDO.

(1) Em uma tabela de incremento automático, obtenha o valor mais alto (nome da minha coluna = id) da coluna incrementada uma vez que cada script seja executado primeiro:

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

(2) Execute a consulta MySQL como faria normalmente e converta o resultado para inteiro, por exemplo:

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

(3) Após a consulta "INSERT INTO ... ON DUPLICATE KEY UPDATE", obtenha o último ID inserido da sua maneira preferida, por exemplo:

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

(4) Compare e reaja: se o lastInsertId for maior que o mais alto da tabela, provavelmente é um INSERT, certo? E vice versa.

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

Eu sei que é rápido e talvez sujo. E é um post antigo. Mas, ei, eu estava procurando uma solução há muito tempo, e talvez alguém ache o meu caminho um pouco útil de qualquer maneira. Muito bem sucedida!


0

apenas maneira simples

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 acordo com o meu SSMS IDE, sua sintaxe não está correta com a forma como você está agrupando sua lógica nos blocos IF BEGIN - END ELSE BEGIN - END.
Erutan409 03/03

0

No primeiro cenário, eu supunha que sua tabela tivesse a coluna 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

No segundo cenário, não é necessário usar a coluna 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

Eu tenho o mesmo problema que alguém pode me ajudar. Veja o link a seguir stackoverflow.com/questions/26043106/…
Ramesh S

0
DECLARE @INSERTEDCOUNT INT,
        @DELETEDCOUNT INT

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

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

SE sua atualização

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

se a sua inserção

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0

0

Eu usei essas exists (select * from inserted/deleted)consultas por um longo tempo, mas ainda não é suficiente para operações CRUD vazias (quando não há registros insertede deletedtabelas). Então, depois de pesquisar um pouco sobre este tópico, encontrei uma solução mais 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'

Também é possível usar columns_updated() & power(2, column_id - 1) > 0para ver se a coluna está atualizada, mas não é seguro para tabelas com grande número de colunas. Usei uma maneira um pouco complexa de calcular (consulte o artigo útil abaixo).

Além disso, essa abordagem ainda classifica incorretamente algumas atualizações como inserções (se todas as colunas da tabela forem afetadas pela atualização) e provavelmente classificará as inserções em que apenas os valores padrão são inseridos como exclusões, mas essas são as operações raras (em locação no meu sistema eles são). Além disso, não sei como melhorar essa solução no 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

Eu faço isso:

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

1 -> inserir

2 -> excluir

3 -> atualização

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;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.