Слияние и триггер - Нарушение первичного ключа

У меня есть эти таблицы

CREATE TABLE [Test](
    [Id] [int] NOT NULL,
    [Value] [int] NOT NULL,
    [Id_Test_2] [int] NOT NULL,
 CONSTRAINT [PK_Test] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]



CREATE TABLE [Test_2](
    [Id_Test_2] [int] NOT NULL,
    [Value_Test_2] [int] NOT NULL,
 CONSTRAINT [PK_Test_2] PRIMARY KEY CLUSTERED 
(
    [Id_Test_2] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

На тестовом столе у ​​меня есть этот триггер обновления:

CREATE TRIGGER [dbo].[Test_TriggerUpdate]
ON [dbo].[Test]

FOR UPDATE 

NOT FOR REPLICATION

AS
BEGIN

MERGE Test_2 AS Target

    USING 
        ( SELECT    D.Id_Test_2 ,
                    ( COALESCE(D.Value, 0) * 2 ) AS Value 
          FROM      Deleted D                    
        ) AS Source
    ON ( Target.Id_Test_2 = Source.Id_Test_2       )
    WHEN MATCHED 
        THEN 
            UPDATE
          SET               TARGET.Value_Test_2 = ( TARGET.Value_Test_2 - Source.Value )
    WHEN NOT MATCHED BY TARGET 
        THEN
        INSERT  (
                      Id_Test_2 ,
                      Value_Test_2

                    )
          VALUES    ( Source.Id_Test_2 ,                      
                      (Source.[Value]*(-1))
                    );



  MERGE Test_2 AS Target
    USING 
        ( SELECT    I.Id_Test_2 ,
                    ( COALESCE(I.Value, 0)
                      * 2 ) AS Value
          FROM      INSERTED I
        ) AS Source
    ON ( Target.Id_Test_2 = Source.Id_Test_2
       )
    WHEN MATCHED 
        THEN 
            UPDATE
          SET               TARGET.Value_Test_2 = ( TARGET.Value_Test_2    + Source.Value )
    WHEN NOT MATCHED BY TARGET 
        THEN
        INSERT  (
                      Id_Test_2 ,
                      Value_Test_2

                    )
          VALUES    ( Source.Id_Test_2 ,
                      Source.[Value]
                    );

END

таблица test_2 пуста и test имеет эту запись

Id  Value   Id_Test_2
1   10  1
2   20  1
3   30  2

когда я запускаю это обновление

UPDATE Test  SET VALUE= 50

у меня такая ошибка

Сообщение 2627, Уровень 14, Состояние 1, Процедура Test_TriggerUpdate, Строка 12 Нарушение ПЕРВИЧНЫЙ КЛЮЧ 'PK_Test_2'. Невозможно вставить дубликат ключа со значением (1) в "Test_2".

Возможно, это происходит, когда операция слияния вызывается с несколькими строками и вместо вызова перед INSERT и следующей операцией UPDATE она запускает два INSERT для записи 1 и 2. Что можно сделать?

1 ответ

Решение

Простой способ решить эту проблему - использовать инструкцию group by для ключа и функцию агрегирования значений для суммирования следующим образом:

ALTER TRIGGER [dbo].[Test_TriggerUpdate]
ON [dbo].[Test]

FOR UPDATE 

NOT FOR REPLICATION

AS
BEGIN

  MERGE Test_2 AS Target
    USING 
        ( SELECT    D.Id_Test_2 ,
                    sum(( COALESCE(D.Value, 0) * 2 )) AS Value
          FROM      Deleted D      
          GROUP BY  Id_Test_2
        ) AS Source
    ON ( Target.Id_Test_2 = Source.Id_Test_2 )
    WHEN MATCHED 
        THEN 
        UPDATE
          SET TARGET.Value_Test_2 = ( TARGET.Value_Test_2 - Source.Value )
    WHEN NOT MATCHED BY TARGET 
        THEN
        INSERT  ( Id_Test_2 ,
                  Value_Test_2
                )
        VALUES  ( Source.Id_Test_2 ,
                  (Source.[Value]*(-1))
                );

  MERGE Test_2 AS Target
    USING 
        ( SELECT    I.Id_Test_2 ,
                    sum(( COALESCE(I.Value, 0) * 2 )) AS Value
          FROM      INSERTED I
          GROUP BY  Id_Test_2  
        ) AS Source
    ON ( Target.Id_Test_2 = Source.Id_Test_2 )
    WHEN MATCHED 
        THEN 
        UPDATE
          SET TARGET.Value_Test_2 = ( TARGET.Value_Test_2 + Source.Value )
    WHEN NOT MATCHED BY TARGET 
        THEN
        INSERT  ( Id_Test_2 ,
                  Value_Test_2
                )
        VALUES  ( Source.Id_Test_2 ,
                  Source.[Value]
                );

END
Другие вопросы по тегам