Используя значения по умолчанию в INSTEAD OF ВСТАВЛЯЮТ триггер

Мы выполняем миграцию базы данных к SQL Server, и поддерживать приложение прежней версии, мы определили представления о таблице SQL Server, которые представляют данные, как приложение прежней версии ожидает.

Однако мы теперь испытываем затруднения из-за INSTEAD OF, ВСТАВЛЯЮТ триггеры, определенные на тех представлениях, когда поля могут иметь значения по умолчанию.

Я попытаюсь дать пример.

Таблица в базе данных имеет 3 поля, a, b, и c. c является совершенно новым, приложение прежней версии не знает об этом, таким образом, у нас также есть представление с 2 полями, a и b.

Когда приложение прежней версии пытается вставить значение в свое представление, мы используем INSTEAD OF, ВСТАВЛЯЮТ триггер в поиск значение, которое должно войти в поле c, что-то вроде этого:

INSERT INTO realTable(a, b, c) SELECT Inserted.a, Inserted.b, Calculated.C FROM...

(Детали поиска не релевантны.)

Этот триггер работает хорошо, если поле b не имеет значение по умолчанию. Это вызвано тем, что если запрос

INSERT INTO legacyView(a) VALUES (123)

выполняется, затем в триггере, Inserted.b является ПУСТЫМ, не значение по умолчанию b. Теперь у меня есть проблема, потому что я не могу сказать различию вышеупомянутый запрос, который поместил бы значение по умолчанию в b и это:

INSERT INTO legacyView(a,b) VALUES (123, NULL)

Даже если b был НЕ ДОПУСКАЮЩИМ NULL-ЗНАЧЕНИЯ, я не знаю, как записать Запрос на вставку в триггере, таким образом, что, если значение было предусмотрено b, оно используется в триггере, но если не значение по умолчанию используется вместо этого.

Править: добавленный, что я не копировал бы значения по умолчанию в триггере. Значения по умолчанию уже находятся в схеме базы данных, я надеялся бы, что мог просто использовать их непосредственно.

6
задан Paul Smith 4 February 2010 в 16:47
поделиться

2 ответа

Пол: Я решил эту проблему; в конце концов. Немного грязное решение и, возможно, не всем придется по вкусу, но я новичок в SQL Server и подобных ему:

В триггере Вместо_INSERT:

  1. Скопируйте структуру данных вставленной виртуальной таблицы во временную таблицу:

     SELECT * INTO aTempInserted FROM Inserted WHERE 1 = 2 
     
  2. Создайте представление для определения ограничений по умолчанию для базовой таблицы представления (из системных таблиц) и используйте их для построения операторов, которые будут дублировать ограничения в временная таблица:

     SELECT 'ALTER TABLE dbo.aTempInserted 
    ADD CONSTRAINT' + dc.name + 'Temp' + 
     'DEFAULT (' + dc.definition + ') {{1} } FOR '+ c.name AS Cmd, OBJECT_NAME (c.object_id) AS Name 
    FROM sys.default_constraints AS dc 
    INNER JOIN sys.columns AS c 
    ON dc.parent_object_id = c.object_id 
    AND dc.parent_column_id = c.column_id 
     
  3. Используйте курсор для итерации по полученному набору и выполнения каждого оператора. Это оставляет вам временную таблицу с теми же значениями по умолчанию, что и таблица, в которую нужно вставить.

  4. Вставить запись по умолчанию во временную таблицу (все поля допускают значение NULL, поскольку они созданы из вставленной виртуальной таблицы):

     INSERT INTO aTempInserted DEFAULT VALUES 
     
  5. Скопировать записи из вставленной виртуальной таблицы в представление базовая таблица (куда они были бы вставлены изначально, если бы триггер не предотвратил это), присоединение к временной таблице для предоставления значений по умолчанию. Это требует использования функции COALESCE, чтобы по умолчанию использовались только неподставленные значения:

     INSERT INTO realTable ([a], [b], 
    SELECT COALESCE (I. [a], T. [a]) , 
    КОАЛЕСС (I. [a], T.[b]) 
    FROM Inserted AS I, 
    aTempInserted AS T 
     
  6. Удалить временную таблицу

1
ответ дан 17 December 2019 в 18:15
поделиться

Некоторые идеи:

  • Если унаследованное приложение указывает списки столбцов для INSERT и именует столбцы, а не использует SELECT *, то не можете ли вы просто привязать значение по умолчанию к столбцу c и позволить приложению использовать вашу исходную (измененную) таблицу?

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

  • Как насчет того, чтобы оставить исходную таблицу в покое и добавить дополнительные столбцы в отдельную таблицу, которая имеет отношение 1-1 к исходной? Затем создайте представление, объединяющее эти две таблицы, и поместите в это новое представление соответствующие триггеры вместо триггеров для обработки всех операций с данными, разделенных между двумя таблицами. Я понимаю, что это влияет на производительность, но, возможно, это единственный способ обойти проблему. Это был бы идеальный случай для материализованного представления, которое замедлило бы обновления, но заставило бы результат работать точно так же, как таблица для чтения. (Материализованные представления лучше всего подходят для внутренних объединений и не требуют агрегации. Они также устанавливают блокировки схемы на исходные таблицы.)

  • Я столкнулся с аналогичной проблемой, когда я не мог отличить преднамеренно NULL-значения от пропущенных. столбцы в триггере вместо UPDATE для представления. В конце концов я сделал триггер вместо INSERT в представлении для преобразования вставок в обновления (если ключ уже существовал, это было обновление, в противном случае это была вставка). Хотя напрямую это вам не поможет, но может подтолкнуть к некоторым идеям у вас или у других.

1
ответ дан 17 December 2019 в 18:15
поделиться
Другие вопросы по тегам:

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