Идентификация операции внутри триггера

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

Ответить
Naidenov
Сообщения: 59
Зарегистрирован: 18 янв 2005, 17:38

Идентификация операции внутри триггера

Сообщение Naidenov » 13 окт 2007, 19:33

Описание проблемы : Есть в наличии 2 таблицы Т1 (master) и Т2 (detail), связанные друг с другом ограничением внешнего ключа. Для внешнего ключа настроены каскадные изменения. На таблице Т2 висит триггер after delete.
Вопрос : Как внутри вышеупомянутого триггера узнать чем было спровоцировано удаление записи? Это результат срабатывания системного триггера, обслуживающего каскадные изменения при удалении записи из таблицы Т1, или же это обычное удаление записи из таблицы Т2 посредством выполнения оператора delete? Можно ли вообще внутри триггера как-то отличить друг от друга эти 2 удаления?
System Info : FireBird 1.5.2 CS

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 13 окт 2007, 19:48

Можно ли вообще внутри триггера как-то отличить друг от друга эти 2 удаления
никак. вопрос - зачем? что такого внутри триггера t2.after_delete, что он должен отличать удаление напрямую от удаления через каскад?
а если это удаление будет не каскадным, а триггером на таблице master - это что, третий случай? А если из триггера вообще другой таблицы - четвертый?

Naidenov
Сообщения: 59
Зарегистрирован: 18 янв 2005, 17:38

Сообщение Naidenov » 13 окт 2007, 20:01

kdv писал(а):что такого внутри триггера t2.after_delete, что он должен отличать удаление напрямую от удаления через каскад?
Ничего особенного. :) Совершенно тривиальное протоколирование изменений таблицы Т2.
А по поводу допустимого множества случаев, то здесь также все просто. Различать нужно только 2 из них: непосредственное удаление и все остальные. :)

Dimitry Sibiryakov
Заслуженный разработчик
Сообщения: 1436
Зарегистрирован: 15 сен 2005, 09:05

Сообщение Dimitry Sibiryakov » 15 окт 2007, 08:09

Ну, это просто: убрать возможность "всех других" и задача решится сама собой. Каскады - зло!

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Re: Идентификация операции внутри триггера

Сообщение WildSery » 15 окт 2007, 10:25

Naidenov писал(а):Описание проблемы :
Проблемы в этом разделе не нашёл.
Naidenov писал(а):Как внутри вышеупомянутого триггера узнать чем было спровоцировано удаление записи?
Выполнять удаление разными пользователями.

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 15 окт 2007, 10:57

Naidenov писал(а):
kdv писал(а):что такого внутри триггера t2.after_delete, что он должен отличать удаление напрямую от удаления через каскад?
Ничего особенного. :) Совершенно тривиальное протоколирование изменений таблицы Т2.
А по поводу допустимого множества случаев, то здесь также все просто. Различать нужно только 2 из них: непосредственное удаление и все остальные. :)
Всё зависит от того, для чего именно нужен этот протокол.
У нас была похожая задача, когда нужно было вести и отображать историю изменений объекта, а объект раскладывался на несколько таблиц.

Т.к. для одного логического изменения использовалась одна, явно стартуемая, транзакция, то решение очевидно - добавить в протокол ID транзакции и группировать по нему.

Naidenov
Сообщения: 59
Зарегистрирован: 18 янв 2005, 17:38

Сообщение Naidenov » 15 окт 2007, 15:32

Tonal писал(а):решение очевидно - добавить в протокол ID транзакции и группировать по нему.
Возможно я чего-то недопонимаю, но мне кажется, что сгруппировав записи по id_transaction, нельзя ответить на вопрос: как отличить непосредственное удаление от всех прочих? Вряд ли мне поможет знание того, что в рамках транзакции TR1 было выполнено удаление DEL1, а в рамках транзакции TR2 было выполнено удаление DEL2. А какое из них "какого сорта"?

P.S. Еще раз прошу прощение, может быть я туго воспринимаю и недогоняю... :roll:

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 15 окт 2007, 17:24

Всё же неясно, нафига. Ну да ладно. Проктологи рекомендуют.

Код: Выделить всё

CREATE TRIGGER CHILD_AD FOR CHILD ACTIVE AFTER DELETE POSITION 0
as
  declare variable FK_Cnt integer;
begin
  select count(*) from parent where id=old.parentid into :FK_Cnt;
  if (FK_Cnt = 0) then
    -- Это каскадное удаление
  else 
    -- Удаление вручную
end

kdv
Forum Admin
Сообщения: 6595
Зарегистрирован: 25 окт 2004, 18:07

Сообщение kdv » 15 окт 2007, 17:45

собственно, проктологические варианты решения должны навевать на мысль, что что-то здесь не так. В триггере обычно нет никакой возможности определить, откуда идет удаление - из процедуры, другого триггера, fk, и так далее.
В репликаторах такая задача решается определением специального имени пользователя, которое и анализируется в триггере для того чтобы отличить операторский и "репликаторский" ввод.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 15 окт 2007, 18:24

kdv писал(а):В триггере обычно нет никакой возможности определить, откуда идет удаление - из процедуры, другого триггера, fk, и так далее.
В случае именно каскадного удаления FK - можно, из-за особенностей удаления (мастер-деталь).
kdv писал(а):...такая задача решается определением специального имени пользователя
Я вперёд сказал :) хоть и другими словами.

Naidenov
Сообщения: 59
Зарегистрирован: 18 янв 2005, 17:38

Сообщение Naidenov » 15 окт 2007, 18:32

Огромное спасибо всем откликнувшимся и не прошедшим мимо. Ваши советы очень помогли! :wink:

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 15 окт 2007, 18:42

Naidenov писал(а):
Tonal писал(а):решение очевидно - добавить в протокол ID транзакции и группировать по нему.
Возможно я чего-то недопонимаю, но мне кажется, что сгруппировав записи по id_transaction, нельзя ответить на вопрос: как отличить непосредственное удаление от всех прочих?
В нашем случае велось протоколирование и мастера и деталя.
Соответственно, если в потоколе для одной транзакции присутствовали записи удаления и мастера и подчинённых детей, стал быть было каскадное удаление.
Если мастер отсутствовал - значит было непосредственное удаление.

Ты бы всё же рассказал, для чего ты этот протокол использовать хочешь - глядишь и решений бы адекватных накидали.
А то, в твоей постановке очень уж практологические решения получаются...

P.S. Да, похоже можно с помощью контекстных переменных извратиться:
1) В триггере мастера before_delete устанавливаем контекстную переменную для контекста транзакции.
2) В триггере мастера after_delete сбрасываем
3) В триггере деталя проверяем - установлена - стал быть удаление по FK.
Ну и эту же переменную можно заиспользовать для индикации что удаляем из ХП или другого триггера. :-)

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 15 окт 2007, 19:21

Tonal писал(а):похоже можно с помощью контекстных переменных извратиться:
Так не получится - After Delete мастера наступит раньше, чем начнут удаляться детали.

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 25 ноя 2007, 11:15

WildSery писал(а):
Tonal писал(а):похоже можно с помощью контекстных переменных извратиться:
Так не получится - After Delete мастера наступит раньше, чем начнут удаляться детали.
Это поведение где-нибудь описано?

Тогда, after delet мастера можно убрать, а и заложиться на то, что в одной транзакции удаления никогда не будет одновременно удаления обоих видов. :-)

Хотя тут тоже есть завязка на то, что триггер before delete будет выполнятся до удаления детей...

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

Ответить