Триггер Вставить обновление, как определить, вставить или обновить


162

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

Ответы:


167

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

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

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

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


Посмотрите на ответ @ MikeTeeVee ниже для полного ответа. Этот неполный.
Лоренц Мейер

1
Оригинальный вопрос @LorenzMeyer не нуждается в этом. У меня также есть EXISTS (SELECT * FROM DELETED). Не уверен, почему вы думаете, что это не завершено ...
ГБН

На что @LorenzMeyer также может ссылаться, так это на утверждение « У вас есть только строки в DELETED при обновлении, но всегда есть строки в INSERTED». Это не всегда так, потому что бывают случаи, когда вызывается триггер Update / Insert и вызывается INSERTED. опорожнить. В своем ответе я объясняю, как это может быть вызвано предикатом, исключающим изменение любых данных. В этом случае Trigger по-прежнему вызывается для попытки DML, но таблицы DELETED и INSERTED пусты. Это связано с тем, что SQL по-прежнему учитывает время, когда вы хотите регистрировать каждую попытку DML (даже если они не изменяют никаких данных).
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
Мне также нравится писать SELECT 1 FROM INSERTED, так как я думаю, что это более четко сигнализирует о намерениях, но я бы разочаровался программистами MSSQL, если это что-то
изменит

26
IF EXISTS (SELECT * ...) и IF EXISTS (SELECT 1) ... имеют одинаковую производительность. Строка не читается и не извлекается вообще. Фактически вы также можете использовать IF EXISTS (SELECT 1/0 ...), и он все равно будет работать и не приведет к ошибке деления на ноль.
Endrju

1
Я создавал отдельные триггеры для вставки, обновления и удаления. Теперь здорово знать, что они могут быть объединены!
UJS

2
Если кто-то пишет запрос в INSERT и удаляет две разные строки (вставьте новую строку и удалите другую строку в том же сценарии), возможно, триггер, настроенный вышеописанным способом, фактически идентифицирует это как ОБНОВЛЕНИЕ (даже если намерение на самом деле не является обновлением) из-за наличия данных в INSERTED / DELETED sql-таблицах?
июня

87

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

Используйте это, чтобы быть в безопасности:

--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 за их ответы.
Я построил это из своих сценариев.


3
Это приз, обработка несуществующих удален. Хорошая работа!
Эндрю Вулф,

6
У нас также может быть ОБНОВЛЕНИЕ, которое не затрагивает строки (или даже ВСТАВКА).
Разван

@AndrewWolfe? Что ты говоришь? В вопросе конкретно говорится, что «мне нужно написать триггер вставки и обновления в таблицу A» . Ничего о триггерах УДАЛИТЬ.
ypercubeᵀᴹ

@ ypercubeᵀᴹ извините, около 80% моих триггеров покрывают все три времени.
Эндрю Вулф

18

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

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
этот неправильно определяет операторы, которые ничего не вставляют или ничего не обновляют.
Роман Пекарь

11

После долгих поисков я не смог найти точный пример одного триггера SQL Server, который обрабатывает все (3) три условия действий триггера INSERT, UPDATE и DELETE. Наконец, я нашел строку текста, в которой говорилось о том, что при возникновении DELETE или UPDATE общая таблица DELETED будет содержать запись для этих двух действий. На основании этой информации я создал небольшую подпрограмму 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   

9

Я считаю, что вложенные, если немного сбивают с толку и:

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

;)

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

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

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

хотя мне также нравится ответ, опубликованный @Alex, я предлагаю этот вариант решения @ Грэма выше

он использует исключительно существование записей в таблицах INSERTED и UPDATED, в отличие от использования COLUMNS_UPDATED для первого теста. Это также предоставляет параноику программисту облегчение, зная, что окончательный случай был рассмотрен ...

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

вы получите NOOP с заявлением, подобным следующему:

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

Кажется, что первый ENDотступ с ошибкой! (вызывает вопрос, где первое BEGINзакрыто)
S.Serpooshan

else if и final else содержат отдельные операторы. начало и конец действительно не нужны, поскольку IF / Else - это одно утверждение. Я исправил отступ. Спасибо за помощь.
Грег

3

Это может быть более быстрый способ:

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
Этот способ не работает для таблиц с большим количеством столбцов, так как columns_updated () возвращает огромную переменную. Таким образом, «> 0» терпит неудачу , потому что 0 по умолчанию для внутренне хранимого числа намного меньше , чем значение возвращенного из COLUMNS_UPDATED ()
Graham

3

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


2

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

Это должно быть IF COLUMNS_UPDATED () < > 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

1

Это делает трюк для меня:

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

Поскольку не все столбцы могут быть обновлены одновременно, вы можете проверить, обновляется ли определенный столбец чем-то вроде этого:

IF UPDATE([column_name])

Задача с этим решением заключается в том, что вы должны знать имя столбца. Некоторые другие разработаны так, что вы можете просто скопировать вставку из библиотеки фрагментов. Небольшой момент, но, учитывая все обстоятельства, общее решение лучше, чем решение для конкретного случая. ПО МОЕМУ МНЕНИЮ.
Грег

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
Я бы не использовал COUNT (*) по соображениям производительности - он должен сканировать всю таблицу. Вместо этого я бы установил флаг, используя IF EXISTS (SELECT * FROM INSERTED), то же самое для DELETED. Обычно я знаю, что затронуто только несколько строк, но зачем тормозить систему.
Endrju

Я собирался опубликовать что-то очень похожее в качестве решения. Это немного многословно, но очень читабельно. Справедливый обмен. Мне также нравится решение Грамса выше.
Грег

1

Мне нравятся решения, которые "элегантны в компьютерных науках". Мое решение здесь обращается к псевдотаблям [вставлено] и [удалено] один раз, чтобы получить их статусы, и помещает результат в битовую переменную. Тогда каждая возможная комбинация INSERT, UPDATE и DELETE может быть легко протестирована в течение всего триггера с эффективными двоичными оценками (за исключением маловероятной комбинации INSERT или DELETE).

Предполагается, что не имеет значения, каким был оператор DML, если строки не были изменены (что должно удовлетворять подавляющему большинству случаев). Так что, хотя оно не так полно, как решение Романа Пекара, оно более эффективно.

При таком подходе у нас есть возможность одного триггера «FOR INSERT, UPDATE, DELETE» на таблицу, что дает нам A) полный контроль над порядком действий и b) одну реализацию кода на действие, применимое к нескольким действиям. (Очевидно, что каждая модель реализации имеет свои плюсы и минусы; вам нужно будет оценивать свои системы индивидуально на предмет того, что действительно работает лучше всего.)

Обратите внимание, что операторы «существует (выберите * из« вставлено / удалено »)» очень эффективны, поскольку нет доступа к диску ( 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

Спасибо за это решение, которое подходит в моем контексте. Вы бы порекомендовали способ обновить столбец LastUpdated обновленной / вставленной строки? Вы также порекомендовали бы способ сохранить в другой таблице идентификатор удаленной строки (может быть составной ключ)?
Себастьян

0

Быстрое решение MySQL

Кстати: я использую MySQL PDO.

(1) В таблице автоинкремента просто получите самое высокое значение (имя моего столбца = id) из увеличенного столбца, как только каждый сценарий будет запущен первым:

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

(2) Запустите запрос MySQL, как обычно, и приведите результат к целому числу, например:

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

(3) После запроса «INSERT INTO ... ON DUPLICATE KEY UPDATE» получите последний вставленный идентификатор вашего предпочтительного способа, например:

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

(4) Сравните и отреагируйте: если lastInsertId выше самого высокого в таблице, это, вероятно, INSERT, верно? И наоборот.

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

Я знаю, что это быстро и, возможно, грязно. И это старый пост. Но, эй, я долго искал решение, и, возможно, кто-то найдет мой путь в любом случае полезным. Всего наилучшего!


0

простой способ

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 

Согласно моей IDE SSMS, ваш синтаксис не соответствует тому, как вы оборачиваете свою логику в блоки IF BEGIN - END ELSE BEGIN - END.
Erutan409

0

В первом сценарии я предположил, что в вашей таблице есть столбец 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

Во втором сценарии не нужно использовать столбец 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

У меня та же проблема, кто-то может мне помочь. См. Следующую ссылку stackoverflow.com/questions/26043106/…
Рамеш С.

0
DECLARE @INSERTEDCOUNT INT,
        @DELETEDCOUNT INT

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

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

Если его обновление

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 1

если его вставка

 @INSERTEDCOUNT = 1
 @DELETEDCOUNT = 0

0

Я использовал эти exists (select * from inserted/deleted)запросы в течение длительного времени, но этого все еще недостаточно для пустых операций CRUD (когда в таблицах insertedи записях нет записей deleted). Поэтому, немного изучив эту тему, я нашел более точное решение:

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'

Также можно использовать, columns_updated() & power(2, column_id - 1) > 0чтобы увидеть, обновлен ли столбец, но это небезопасно для таблиц с большим количеством столбцов. Я использовал немного сложный способ расчета (см. Полезную статью ниже).

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


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

я сделаю это:

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

1 -> вставить

2 -> удалить

3 -> обновить

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;
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.