Триггер: на вставленное значение столбца таблицы нельзя ссылаться внутри условия - PullRequest
1 голос
/ 19 февраля 2020

У меня есть следующий триггер:

    GO
ALTER TRIGGER [dbo].[FooTrigger]
ON [dbo].[Foo]
FOR INSERT
AS 
DECLARE @Url nvarchar(MAX)
SELECT @Url  = ins.Url from INSERTED ins;

IF EXISTS (SELECT *  
           FROM [dbo].[Foo]  p   
           where p.Url =  @Url)  
BEGIN  
RAISERROR ( 'error %s' , 16, 1, @Url)
ROLLBACK TRANSACTION;  
RETURN   
END;
PRINT 'Executed Trigger Insert.'
GO

Это работает без проблем, если я жестко закодировал условие, подобное

IF EXISTS(SELECT * FROM [dbo].[Foo] foo WHERE foo.Url = 'test.com')

, но оригинальный не работает. Выдает сообщение:

Похоже, что существует ошибка при ссылке на вставленные данные таблицы с использованием @Url внутри условия. Но RAISERROR ( @Url , 16, 1); правильно печатает значение, поэтому я думаю, что внутри условия ссылка как-то не разрешена. Есть ли обходные пути?

1 Ответ

3 голосов
/ 19 февраля 2020

Во-первых, если вы пытаетесь гарантировать, что url s уникальны, то используйте ограничение уникальности:

alter table foo add constraint unq_foo_url unique (url);

Во-вторых, НИКОГДА не предполагайте, что inserted имеет только одну строку. Этот код:

SELECT @Url  = ins.Url from INSERTED ins;

- это ошибка, ожидающая своего появления.

РЕДАКТИРОВАТЬ:

Если у вас есть таблица, которая уже нарушает ограничение уникальности, у вас возникла грязная проблема. Вы можете решить эту проблему. Проблема с вашим кодом в том, что триггер FOR INSERT действительно является триггером AFTER INSERT - новые данные уже видны.

Но вы можете справиться с этим. Вот один из способов:

CREATE TRIGGER [dbo].[FooInsertTrigger]
ON [dbo].[Foo]
AFTER INSERT
AS
BEGIN
    IF (SELECT TOP (1) COUNT(*)
        FROM inserted i JOIN
             [dbo].[Foo] f
             on i.url = f.url 
        GROUP BY i.url
        ORDER BY COUNT(*) DESC
       ) > 1
    BEGIN  
        RAISERROR('Duplicate URL found' , 16, 1);
        ROLLBACK TRANSACTION;  
        RETURN   
    END;
    PRINT 'Executed Trigger Insert.'
END;

Максимум count(*) больше 1 в следующих случаях:

  • inserted.url соответствует 1 строке в foo. Это нормальный случай.
  • inserted.url соответствует нескольким строкам в foo. Это поймано.

Это также поймает случай, когда есть кратные числа в inserted.

Делая код безопасным для нескольких вставок, труднее вытащить оскорбительные URL. Но ошибка обрабатывается правильно.

Здесь - это db <> скрипка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...