Триггерный цикл SQL Server

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

Например:

  • У меня есть два пользовательские таблицы, users_V1 и users_V2, Когда пользователь обновляется с одним из приложения V1, оно активирует триггер, обновляющий его в users_V2 также.

  • Если я захочу добавить, что то же включает таблицу V2 для обновления данных в V1, когда пользователь будет обновлен в V2, то это войдет в бесконечный цикл? Есть ли любой способ избежать этого.

8
задан Roch 11 February 2010 в 09:31
поделиться

8 ответов

Не рекомендую явного отключения триггера во время обработки - это может привести к странным побочным эффектам.

Наиболее надежным способом обнаружения (и предотвращения) циклов в триггере является использование CONTEXT_INFO().

Пример:

CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS

DECLARE @ctx VARBINARY(128) 
SELECT @ctx = CONTEXT_INFO() 
IF @ctx = 0xFF
    RETURN

SET @ctx = 0xFF

-- Trigger logic goes here

Смотрите по этой ссылке для более подробного примера.


Примечание на CONTEXT_INFO() в SQL Server 2000:

Поддерживается контекстная информация, но, очевидно, функция CONTEXT_INFO не поддерживается. Вместо этого нужно использовать:

SELECT @ctx = context_info
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
8
ответ дан 5 December 2019 в 08:52
поделиться

У меня нет триггеров для этого конкретного сценария разработки. Сказав это, с ограниченными знаниями о том, что делает ваше приложение и почему оно это делает, вот мой общий анализ:

Использование триггера в таблице имеет то преимущество, что я могу воздействовать на все действия в таблице. Вот и все, ваше главное преимущество в данном случае. Но это будет означать, что у вас есть пользователи с прямым доступом к таблице или с несколькими точками доступа к таблице. Я стараюсь этого избегать. У триггеров есть свое место (я их часто использую), но это один из последних инструментов проектирования баз данных, который я использую, потому что они, как правило, мало знают о своем контексте (как правило, сильные стороны) и когда используются там, где им действительно нужно чтобы знать о различных контекстах и ​​общих сценариях использования, их преимущества ослаблены.

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

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

Я предпочитаю больше контролировать интерфейс с базовыми таблицами - с помощью представлений, UDF или SP. Пользователи никогда не получают прямого доступа к таблице. Другой момент здесь заключается в том, что вы можете представить единый «пользовательский» VIEW или UDF, объединяющий соответствующие базовые таблицы, даже не зная об этом, - возможно, дойдя до точки, где даже не требуется «синхронизация», поскольку новые атрибуты находятся в Система EAV, если вам нужна такая патологическая гибкость или какая-то другая структура, которую еще можно объединить, - скажем, ВНЕШНИЙ ПРИМЕНИТЬ UDF и т. Д.

1
ответ дан 5 December 2019 в 08:52
поделиться

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

update t
set field1 = i.field1
field2 = i.field2
from inserted i
join table1 t on i.id  = t.id
where field1 <> i.field1 OR field2 <> i.field2
0
ответ дан 5 December 2019 в 08:52
поделиться

Используйте SQLite. Затем можно запросить определенные данные и сохранить локальный файл. К вашему сведению - предложение PDO автоматически добавляет отдельные предложения вокруг значения.

$Filename = "MyDB.db";

try {
    $SQLHandle = new PDO("sqlite:".$Filename);
}
catch(PDOException $e) {
    echo $e->getMessage()." :: ".$Filename;
}

$SQLHandle->exec("CREATE TABLE IF NOT EXISTS MyTable (ID INTEGER PRIMARY KEY, MyColumn TEXT)");

$SQLHandle->beginTransaction();

$SQLHandle->exec("INSERT INTO MyTable (MyColumn) VALUES (".$SQLHandle->quote("MyValue").")");
$SQLHandle->exec("INSERT INTO MyTable (MyColumn) VALUES (".$SQLHandle->quote("MyValue 2").")");

$SQLHandle->commit();

$Iterator = $SQLHandle->query("SELECT * FROM MyTable ORDER BY MyColumn ASC");

unset($SQLHandle);

foreach($Iterator as $Row) {
    echo $Row["MyColumn"]."\n";
}
-121--3439009-

Ваша проблема звучит как проблема построения графика . Имена считаются узлами, а членство в наборе - краями. С этой точки зрения требуется структура данных, которая хорошо обрабатывает разреженные графики, например список смежности . Это, конечно, сходно с тем, что вы уже делаете с Dictionary < строка, IEnumerable < строка > > , но размышление об этом таким образом может привести к некоторым полезным реализациям и алгоритмам.

-121--3753588-
  • Либо используйте TRIGGER _ NESTLEVEL () для ограничения рекурсии триггера, либо

  • проверьте в целевой таблице, необходимо ли вообще обновление:

     IF (SELECT COUNT (1)
    ОТ users_V1
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ вставлено ВКЛ. Users_V1.ID = вставлено. Я бы
    WHERE users_V1.field1 < > вставления.field1
    OR users_V1.field2 < > inserted.field2) > 0 BEGIN
    
    ОБНОВИТЬ НАБОР users_V1...
    
6
ответ дан 5 December 2019 в 08:52
поделиться

См. раздел Преобразование чисел в wiki Haskell

-121--4213394-

Невозможно преобразовать конфигурации XML с пользовательскими пространствами имен (например, http://www.springframework.org/schema/security ). Однако конфигурации XML можно смешивать с конфигурациями на основе Java с помощью @ ImportResource

-121--4012967-

В триггере необходимо создать некое обнаружение закольцовывания. Возможно, для проверки существования записи перед ее вводом в следующую таблицу используется оператор «if exists». Это звучит так, как будто он перейдет в бесконечный цикл, путь он сейчас настроен.

0
ответ дан 5 December 2019 в 08:52
поделиться

Избегайте триггеров, подобных чуме .... используйте хранимую процедуру для добавления пользователя. Если для этого требуются некоторые изменения в конструкции, внесите их. Триггеры - это ЗЛО.

0
ответ дан 5 December 2019 в 08:52
поделиться

Рекурсия в триггерах, то есть один триггер вызывает другой, ограничена 32 уровнями

В каждом триггере просто проверьте, существует ли уже строка, которую вы хотите вставить.

Пример

CREATE TRIGGER Table1_Synchronize_Update ON [Table1] FOR UPDATE AS
BEGIN
  UPDATE  Table2
  SET     LastName = i.LastName
          , FirstName = i.FirstName
          ,  ... -- Every relevant field that needs to stay in sync
  FROM    Table2 t2
          INNER JOIN Inserted i ON i.UserID = t2.UserID
  WHERE   i.LastName <> t2.LastName
          OR i.FirstName <> t2.FirstName
          OR ... -- Every relevant field that needs to stay in sync
END

CREATE TRIGGER Table1_Synchronize_Insert ON [Table1] FOR INSERT AS
BEGIN
  INSERT INTO Table2
  SELECT i.*
  FROM   Inserted i
         LEFT OUTER JOIN Table2 t2 ON t2.UserID = i.UserID
  WHERE  t2.UserID IS NULL
END

CREATE TRIGGER Table2_Synchronize_Update ON [Table2] FOR UPDATE AS
BEGIN
  UPDATE  Table1
  SET     LastName = i.LastName
          , FirstName = i.FirstName
          ,  ... -- Every relevant field that needs to stay in sync
  FROM    Table1 t1
          INNER JOIN Inserted i ON i.UserID = t1.UserID
  WHERE   i.LastName <> t1.LastName
          OR i.FirstName <> t1.FirstName
          OR ... -- Every relevant field that needs to stay in sync
END

CREATE TRIGGER Table2_Synchronize_Insert ON [Table2] FOR INSERT AS
BEGIN
  INSERT INTO Table1
  SELECT i.*
  FROM   Inserted i
         LEFT OUTER JOIN Table1 t1 ON t1.UserID = i.UserID
  WHERE  t1.UserID IS NULL
END
0
ответ дан 5 December 2019 в 08:52
поделиться

У меня была точно такая же проблема. Я пробовал использовать CONTEXT_INFO (), но это переменная сеанса, поэтому она работает только в первый раз! Тогда в следующий раз, когда во время сеанса сработает триггер, это не сработает. Итак, я закончил тем, что использовал переменную, которая возвращает уровень вложенности в каждом из затронутых триггеров для выхода.

Пример:

CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS
BEGIN
      --Prevents Second Nested Call
      IF @@NESTLEVEL>1 RETURN 

      --Trigger logic goes here
END

Примечание. Или используйте @@ NESTLEVEL> 0, если вы хотите остановить все вложенные вызовы.

Еще одно замечание. В этой статье, кажется, много путаницы в отношении вложенных вызовов и рекурсивных вызовов. Исходный плакат имел в виду вложенный триггер, где один триггер вызывал срабатывание другого триггера, что приводило к повторному срабатыванию первого триггера и так далее. Это вложенный, но, согласно SQL Server, не рекурсивный, потому что триггер не вызывает / не запускает себя напрямую. Рекурсия НЕ там, где «один триггер [вызывает] другой». Это вложено, но не обязательно рекурсивно. Вы можете проверить это, включив / отключив рекурсию и вложение с некоторыми настройками, упомянутыми здесь: сообщение в блоге о вложении

4
ответ дан 5 December 2019 в 08:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: