Создание триггера сервера SQL к переходу из натурального ключа к суррогатному ключу
-
30-09-2019 - |
Вопрос
Бросок
На работе, где мы планируем обесценение натурального ключевого столбца в одной из наших основных таблиц. Проект состоит из 100+ приложений, ссылающихся на эту таблицу / столбец; 400+ хранимых процедур, которые ссылаются на этот столбец напрямую; и огромный массив общих таблиц между этими приложениями, которые также ссылаются на этот столбец.
Большой взрыв и начать с нуля методов находятся из картины. Мы собираемся обесценить эту колонку одно приложение за раз, подтверждают изменения и переходите к следующему ... и у нас есть длительная целевая цель, чтобы приложить это усилие.
Проблема, которую у меня есть, так это то, что многие из этих приложений имеют общие хранимые процедуры и таблицы. Если я полностью преобразую все приложения A в таблицах / хранимых процедурах приложения B и C будут разбиты до преобразования. Они, в свою очередь, могут нарушать приложения D, E, F ... etc. У меня уже есть стратегия, реализованная для классов кода и хранимых процедур, часть я застрял, это переходное состояние базы данных.
Вот а базовый Пример того, что у нас есть:
Users
---------------------------
Code varchar(32) natural key
Access
---------------------------
UserCode varchar(32) foreign key
AccessLevel int
И мы стремимся сейчас только для переходного состояния, как это:
Users
---------------------------
Code varchar(32)
Id int surrogate key
Access
---------------------------
UserCode varchar(32)
UserID int foreign key
AccessLevel int
Идея, находящаяся в том, что во время переходных фазе не перенесенные приложениями и хранимыми процедурами все еще смогут получить доступ к всем соответствующим данным, и новые могут начать толкать в правильные столбцы - после завершения миграции для всех сохраненных процедур и приложений, которые мы можем, наконец, Снимите дополнительные столбцы.
Я хотел использовать триггеры SQL Server, чтобы автоматически перехватить любую новую вставку / обновление и сделать что-то вроде следующего на каждом из затронутых таблиц:
CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT(, UPDATE)
AS
BEGIN
DIM @code as Varchar(32)
DIM @id as int
SET @code = (SELECT inserted.code FROM inserted)
SET @id = (SELECT inserted.code FROM inserted)
-- This is a migrated application; find the appropriate legacy key
IF @code IS NULL AND @id IS NOT NULL
SELECT Code FROM Users WHERE Users.id = @id
-- This is a legacy application; find the appropriate surrogate key
IF @id IS NULL AND @code IS NOT NULL
SELECT Code FROM Users WHERE Users.id = @id
-- Impossible code:
UPDATE inserted SET inserted.code=@code, inserted.id=@id
END
Вопрос
2 огромных задачи, которые я до сих пор:
- Я не могу сделать «после вставки», потому что нулевые ограничения сделают вставку сбой.
- «Невозможный код», который я упомянул, как я хотел бы чисто прокси оригинальный запрос; Если исходный запрос имеет x, y, z столбцы в нем или просто х, я в идеале хотел бы тот же триггер, чтобы сделать это. И если я добавлю / удалю другой столбец, я бы хотел, чтобы триггер оставаться функциональным.
У кого-нибудь есть код примера, где это может быть возможно, или даже альтернативное решение для сохранения этих столбцов правильно заполнено, даже когда только один из значений передается на SQL?
Решение 3
После сна, это, кажется, это самое общее / повторное использоемое решение, которое я мог бы придумать в синтаксисе SQL. Он отлично работает, даже если оба столбца имеют не нулевую сдержанность, даже если вы не ссылаетесь на «другой» столбец вообще в вашей вставке.
CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT
AS
BEGIN
/*-- Create a temporary table to modify because "inserted" is read-only */
/*-- "temp" is actually "#temp" but it throws off stackoverflow's syntax highlighting */
SELECT * INTO temp FROM inserted
/*-- If for whatever reason the secondary table has it's own identity column */
/*-- we need to get rid of it from our #temp table to do an Insert later with identities on */
ALTER TABLE temp DROP COLUMN oneToManyIdentity
UPDATE temp
SET
UserCode = ISNULL(UserCode, (SELECT UserCode FROM Users U WHERE U.UserID = temp.UserID)),
UserID = ISNULL(UserID, (SELECT UserID FROM Users U WHERE U.UserCode = temp.UserCode))
INSERT INTO Access SELECT * FROM temp
END
Другие советы
Хитрый бизнес ...
Хорошо, прежде всего: этот триггер будет НЕТ Работа во многих обстоятельствах:
SET @code = (SELECT inserted.code FROM inserted)
SET @id = (SELECT inserted.code FROM inserted)
Триггер может быть вызван с набором строк в Inserted
псевдо таблицы - какой из них вы собираетесь выбрать здесь ?? Вам нужно написать ваш триггер таким образом, что он будет работать, даже когда вы получаете 10 строк в Inserted
стол. Если оператор SQL вставляет 10 строк, ваш триггер будет нет быть уволенным в десять раз - один для каждого ряда - но только однажды Для всей пакеты - вам нужно принять это во внимание!
Второй момент: я бы попытался сделать идентификатор IDENTITY
Поля - тогда они всегда получат значение - даже для «устаревших» приложений. Эти «старые» приложения должны предоставлять вместо этого наследие - так что вы должны быть там хорошими. Единственная проблема, которую я вижу и не знаю, как вы справляетесь с тем, что это вставки из уже преобразованного приложения - делают ли они наследие «Старый стиль» наследие? Если нет - как быстро вам нужно иметь такой ключ?
Я думаю, что я думаю, будет «работой очистки», которая будет проходить через стол и получить все строки с нулевым устаревшим ключом, а затем обеспечить для него некоторое значимое значение. Сделайте эту регулярную хранимую процедуру и выполните ее каждый день, четыре часа, 30 минут - все, что подходит для ваших потребностей. Тогда вам не нужно иметь дело с триггерами и всеми ограничениями, которые у них есть.
Разве невозможно было бы сделать схему изменения «BigBang», но создайте виды на вершину этих таблиц, которые «скрыть» изменение?
Я думаю, что вы можете обнаружить, что вы просто откладываете прерывки в более поздний момент времени: «Мы собираемся обесценить эту колонку одно приложение за раз» - это может быть моим наимелостью, но я не вижу, как это когда-либо работать.
Конечно, худший беспорядок может возникнуть, когда разные приложения делают вещи по-разному?