2015-10-16 2 views
0

Гораздо сложнее, чем это, но это основнойSQL Server: удалить строку, если нет ошибки ограничения

  • Person таблица (идентификатор, имя, EMAILADDRESS)
  • Salesperson таблица (идентификатор, PersonId)
  • CustomerServiceRep таблицы (идентификатор, PersonId)

Джефф продавец (ID = 4) и customerservicerep (ID = 5) с PersonId = 1.

Простой

Trigger on SalesPerson Table 
AFTER DELETE 
AS 
    DECLARE @personID int = (SELECT personID FROM deleted); 

    IF @personID IS NOT NULL 
    BEGIN TRY 
     DELETE FROM Person 
     WHERE Person.id = @personID; 
    END TRY 
    BEGIN CATCH 
    END CATCH 

DELETE FROM SalesPerson WHERE id=4; 

Причины

Msg 3616, Level 16, State 1
ошибка была поднята во время выполнения триггера. Партия была прервана, и транзакция пользователя, если она есть, была отброшена назад.

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

ответ

0

Вам нужно вместо триггера delete вместо триггера.

CREATE Trigger tr_Delete_person 
on Person 
INSTEAD OF DELETE 
AS 
BEGIN 
    SET NOCOUNT ON; 

-- Delete any child records 

    Delete FROM SalesPerson 
    WHERE EXISTS (SELECT 1 FROM deleted 
        WHERE personID = SalesPerson.personID) 


    Delete FROM CustomerServiceRep 
    WHERE EXISTS (SELECT 1 FROM deleted 
        WHERE personID = CustomerServiceRep .personID) 


-- Finally delete from the person table 
    DELETE FROM Person 
    WHERE EXISTS (SELECT 1 FROM deleted 
        WHERE personID = Person .personID) 


END 
+0

Кажется, что он избавится от всего, что связано с человеком «jeff». Только желание удалить продавца «jeff», а не customerervicerep «jeff», и если человек «jeff» все еще существует, потому что «jeff» также является клиентом, также не удаляйте человека. Я чувствую, что должен быть более простой способ сделать это, не добавляя каждую таблицу, которая могла бы иметь personID, чтобы проверить, существует ли она там до удаления пользователя «jeff» – user2828970

+0

@ user2828970, я бы установил внешний ключ между 'Person' и 'SalesPerson' с удалением каскада.И внешний ключ между 'Person' и' CustomerServiceRep' без каскада. Если есть больше таблиц, которые ссылаются на «Лицо», вам нужно настроить внешние ключи с соответствующим каскадным действием для всех из них. –

+0

Я вижу, как это может быть полезно. Каскад - это новое для меня. Однако, глядя на него, я еще не видел, как заставить его не удалять человека, если он существует в другой таблице. Таблицы SalesPerson и CustomerServiceRep в основном равны (без приоритета над каждым). «jeff» можно удалить из customerServiceRep так же, как удалить из SalesPerson, но если он все еще существует где-то, человек не должен удаляться. Очевидно, что с длинными строками кода это можно проверить. – user2828970

0

У вас также есть фундаментальный недостаток в вашем триггером в том, что вы, кажется, ожидать, что триггер будет срабатывать один раз в строке - это НЕ случае в SQL Server. Вместо этого триггер запускает один раз за оператор, а псевдо-таблица Deleted может содержать несколько строк.

Учитывая, что эта таблица может содержать несколько строк, которые вы ожидаете, будут выбраны здесь?

DECLARE @personID int = (SELECT personID FROM deleted); 

Это не определено - вы получите значение из одного, произвольно ряд в Deleted, а все остальные игнорируются - обычно не то, что вы хотите!

Вам необходимо переписать весь триггер со знанием DeletedWILL содержит несколько строк! Вам нужно работать с множеством операций - не ожидайте только одной строки в Deleted!

Смежные вопросы