Триггер Вставить обновление, как определить, вставить или обновить - PullRequest
145 голосов
/ 12 апреля 2009

Мне нужно написать триггер вставки и обновления для таблицы A, который удалит все строки из таблицы B, один столбец (скажем, Desc) имеет значения, подобные значению, вставленному / обновленному в столбце таблицы A (например, Col1). Как бы мне написать его так, чтобы я мог обрабатывать как обновления, так и вставки. Как определить, выполняется ли триггер для обновления или вставки.

Ответы [ 20 ]

150 голосов
/ 12 апреля 2009

Если это MS SQL Server ...

Триггеры имеют специальные таблицы INSERTED и DELETED для отслеживания данных «до» и «после». Таким образом, вы можете использовать что-то вроде IF EXISTS (SELECT * FROM DELETED) для обнаружения обновления. При обновлении у вас есть только строки в DELETED, но всегда есть строки в INSERTED.

Ищите «вставлено» в CREATE TRIGGER

Редактировать, 23 ноября 2011 г.

После комментария этот ответ предназначен только для триггеров INSERTED и UPDATED.
Очевидно, что триггеры DELETE не могут иметь «всегда строк в INSERTED», как я уже говорил выше

115 голосов
/ 15 сентября 2011
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
74 голосов
/ 07 июня 2012

Многие из этих предложений не принимаются во внимание, если вы запускаете оператор удаления, который ничего не удаляет.
Допустим, вы пытаетесь удалить, если идентификатор равен некоторому значению, которого нет в таблице.
Ваш триггер все еще вызывается, но в удаленных или вставленных таблицах ничего нет.

Используйте это для безопасности:

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

Отдельное спасибо @KenDog и @Net_Prog за их ответы.
Я построил это по их сценариям.

17 голосов
/ 06 января 2015

Я использую следующее, оно также правильно определяет операторы удаления, которые ничего не удаляют:

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;
11 голосов
/ 12 апреля 2011

После долгих поисков я не смог найти точный пример одного триггера SQL Server, который обрабатывает все (3) три условия действий триггера INSERT, UPDATE и DELETE. Наконец, я нашел строку текста, в которой говорилось о том, что когда происходит УДАЛЕНИЕ или ОБНОВЛЕНИЕ, общая таблица УДАЛЕННАЯ будет содержать запись для этих двух действий. На основании этой информации я создал небольшую подпрограмму Action, которая определяет причину активации триггера. Этот тип интерфейса иногда необходим, когда для триггера INSERT vs. UPDATE необходимо выполнить как общую конфигурацию, так и специальное действие. В этих случаях создание отдельного триггера для ОБНОВЛЕНИЯ и ВСТАВКИ станет проблемой обслуживания. (т. е. правильно ли обновлены оба триггера для необходимого исправления общего алгоритма данных?)

С этой целью я хотел бы предоставить следующий фрагмент кода события мульти-триггера для обработки INSERT, UPDATE, DELETE в одном триггере для 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   
8 голосов
/ 28 июля 2016

Я считаю, что вложенные if немного запутаны и:

Квартира лучше, чем вложенная [Дзен Питона]

;)

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
7 голосов
/ 08 мая 2017
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 голосов
/ 10 августа 2016

Попробуйте это ..

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
3 голосов
/ 03 апреля 2012

Потенциальная проблема с двумя предлагаемыми решениями заключается в том, что в зависимости от того, как они написаны, запрос на обновление может обновить ноль записей, а запрос на вставку может вставить ноль записей. В этих случаях наборы вставленных и удаленных записей будут пустыми. Во многих случаях, если оба набора записей Inserted и Deleted пусты, вы можете просто выйти из триггера, ничего не делая.

2 голосов
/ 25 апреля 2012

Я нашел небольшую ошибку в Grahams, иначе крутое решение:

должно быть IF COLUMNS_UPDATED () <</strong>> 0 - вставить или обновить
вместо> 0 вероятно, потому что верхний бит интерпретируется как целочисленный бит знака SIGNED ... (?). Итого всего:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...