Я хотел бы знать, существует ли так или иначе, я могу добавить триггер на двух таблицах, которые будут копировать данные в другой.
Например:
У меня есть два пользовательские таблицы, users_V1 и users_V2, Когда пользователь обновляется с одним из приложения V1, оно активирует триггер, обновляющий его в users_V2 также.
Если я захочу добавить, что то же включает таблицу V2 для обновления данных в V1, когда пользователь будет обновлен в V2, то это войдет в бесконечный цикл? Есть ли любой способ избежать этого.
Не рекомендую явного отключения триггера во время обработки - это может привести к странным побочным эффектам.
Наиболее надежным способом обнаружения (и предотвращения) циклов в триггере является использование 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
У меня нет триггеров для этого конкретного сценария разработки. Сказав это, с ограниченными знаниями о том, что делает ваше приложение и почему оно это делает, вот мой общий анализ:
Использование триггера в таблице имеет то преимущество, что я могу воздействовать на все действия в таблице. Вот и все, ваше главное преимущество в данном случае. Но это будет означать, что у вас есть пользователи с прямым доступом к таблице или с несколькими точками доступа к таблице. Я стараюсь этого избегать. У триггеров есть свое место (я их часто использую), но это один из последних инструментов проектирования баз данных, который я использую, потому что они, как правило, мало знают о своем контексте (как правило, сильные стороны) и когда используются там, где им действительно нужно чтобы знать о различных контекстах и общих сценариях использования, их преимущества ослаблены.
Если обе версии приложения должны запускать одно и то же действие, они обе должны вызвать одну и ту же сохраненную процедуру.Сохраненная процедура может гарантировать, что вся соответствующая работа выполнена, и когда ваше приложение больше не нуждается в поддержке V1, эту часть сохраненной процедуры можно удалить.
Вызов двух сохраненных процедур в клиентском коде - плохая идея, потому что это уровень абстракции служб данных, который база данных может предоставлять легко и последовательно, не беспокоя ваше приложение об этом.
Я предпочитаю больше контролировать интерфейс с базовыми таблицами - с помощью представлений, UDF или SP. Пользователи никогда не получают прямого доступа к таблице. Другой момент здесь заключается в том, что вы можете представить единый «пользовательский» VIEW или UDF, объединяющий соответствующие базовые таблицы, даже не зная об этом, - возможно, дойдя до точки, где даже не требуется «синхронизация», поскольку новые атрибуты находятся в Система EAV, если вам нужна такая патологическая гибкость или какая-то другая структура, которую еще можно объединить, - скажем, ВНЕШНИЙ ПРИМЕНИТЬ UDF и т. Д.
Попробуйте что-нибудь вроде (я не беспокоился о создании триггера, так как вы явно уже знаете, как написать эту часть):
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
Используйте 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 < строка > >
, но размышление об этом таким образом может привести к некоторым полезным реализациям и алгоритмам.
Либо используйте 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...
См. раздел Преобразование чисел в wiki Haskell
-121--4213394- Невозможно преобразовать конфигурации XML с пользовательскими пространствами имен (например, http://www.springframework.org/schema/security
). Однако конфигурации XML можно смешивать с конфигурациями на основе Java с помощью @ ImportResource
В триггере необходимо создать некое обнаружение закольцовывания. Возможно, для проверки существования записи перед ее вводом в следующую таблицу используется оператор «if exists». Это звучит так, как будто он перейдет в бесконечный цикл, путь он сейчас настроен.
Избегайте триггеров, подобных чуме .... используйте хранимую процедуру для добавления пользователя. Если для этого требуются некоторые изменения в конструкции, внесите их. Триггеры - это ЗЛО.
Рекурсия в триггерах, то есть один триггер вызывает другой, ограничена 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
У меня была точно такая же проблема. Я пробовал использовать 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, не рекурсивный, потому что триггер не вызывает / не запускает себя напрямую. Рекурсия НЕ там, где «один триггер [вызывает] другой». Это вложено, но не обязательно рекурсивно. Вы можете проверить это, включив / отключив рекурсию и вложение с некоторыми настройками, упомянутыми здесь: сообщение в блоге о вложении