Создание триггера SQL Server для перехода от естественного ключа к суррогатному ключу - PullRequest
4 голосов
/ 15 октября 2010

Предыстория

На работе, где мы планируем исключить столбец Natural Key в одной из наших основных таблиц.Проект состоит из более 100 приложений, которые ссылаются на эту таблицу / столбец;400+ хранимых процедур, которые напрямую ссылаются на этот столбец;и обширный массив общих таблиц между этими приложениями, которые также ссылаются на этот столбец.

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

ПроблемаУ меня есть то, что многие из этих приложений имеют общие хранимые процедуры и таблицы.Если я полностью сконвертирую все таблицы / хранимые процедуры Приложения A, Приложения B и C будут прерваны до преобразования.Это, в свою очередь, может сломать приложения D, E, F ... и т. Д.У меня уже есть стратегия, реализованная для классов кода и хранимых процедур, часть, на которой я застрял, - это переходное состояние базы данных.

Вот пример basic того, что мы имеем:

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 огромные проблемы, с которыми я столкнулсядо сих пор:

  1. Я не могу сделать «ПОСЛЕ ВСТАВКИ», потому что ограничения NULL приведут к ошибке вставки.
  2. Упомянутый мною «невозможный код» - это то, как я хотел бы чисто прокси-сервер исходного запроса;Если в исходном запросе есть столбцы x, y, z или просто x, в идеале я бы хотел, чтобы тот же триггер делал это.И если я добавлю / удалю другой столбец, я бы хотел, чтобы триггер оставался работоспособным.

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

Ответы [ 3 ]

2 голосов
/ 15 октября 2010

Хитрый бизнес ...

Хорошо, прежде всего: этот триггер будет НЕ работать во многих случаях:

SET @code = (SELECT inserted.code FROM inserted)
SET @id = (SELECT inserted.code FROM inserted)

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

Второй момент: я попытался бы создать поля IDENTITY идентификатора - тогда они всегда получат значение - даже для «старых» приложений. Эти "старые" приложения должны вместо этого предоставлять устаревший ключ - так что у вас все будет хорошо. Единственная проблема, которую я вижу и не знаю, как вы обрабатываете эти вставки из уже преобразованного приложения - они также предоставляют устаревший ключ "старого стиля"? Если нет - как быстро вам нужен такой ключ?

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

1 голос
/ 15 октября 2010

После сна по проблеме, это, кажется, самое универсальное / повторно используемое решение, которое я мог придумать в рамках синтаксиса SQL.Он отлично работает, даже если оба столбца имеют ограничение NOT NULL, даже если вы вообще не ссылаетесь на «другой» столбец в вашей вставке.

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
1 голос
/ 15 октября 2010

Разве нельзя было бы внести изменения в схему "bigbang", но создать представления поверх тех таблиц, которые "скрывают" изменения?

Я думаю, вы, возможно, обнаружите, что просто откладываете поломки на более поздний момент времени: «Мы собираемся осудить этот столбец по одному приложению за раз» - это может быть моей наивностью, но я не понимаю, как это когда-нибудь сработает.

Конечно, может случиться еще и еще больший беспорядок, когда разные приложения работают по-разному?

...