Триггеры Oracle - проблема с видоизменяющимися таблицами

Мои таблицы:

TableA (id number, state number)
TableB (id number, tableAId number, state number)
TableC (id number, tableBId number, state number)

Таким образом, объекты в TableC являются детьми TableB, и объекты в TableB являются детьми TableA. Наоборот - объекты в TableA являются родителями TableB, и объекты в TableB являются родителями TableC.

Я хотел бы управлять состоянием родительских элементов... скажем, например, что у нас есть эти данные:

TableA (id, state): 
1, 40

TableB (id, tableAId, state): 
1, 1, 40
2, 1, 60

TableC (id, tableBId, state): 
1, 1, 40
2, 1, 50
3, 2, 60
4, 2, 70

Метрополия всегда должна hvae самое маленькое состояние его детей. Таким образом, если мы теперь обновляем TableC как это:

update TableC set state = 50 where Id = 1;

мой триггер должен автоматически обновить TableB (состояние набора = 50 где идентификатор = 1) и затем обновлять также TableA (состояние набора = 50 где идентификатор = 1)

Я хотел бы сделать это с триггерами (ПОСЛЕ ТОГО, КАК ОБНОВЛЕНИЕ, ВСТАВЬТЕ, УДАЛИТЕ, на TableA, TableB, TableC), так, чтобы после того, как каждое действие, это ступает, выполнилось бы:

  1. получите родительский идентификатор
  2. найдите самое маленькое состояние от всех детей текущего родителя
  3. если самое маленькое состояние всех детей больше, чем состояние родителя, то обновите родителя

Как я могу постараться не 'видоизменять ошибку таблицы'? Это, сохраняют для использования автономных транзакций в этом примере? Я видел некоторые мнения, та ошибка таблицы видоизменения указывает на дефекты в логике приложения - действительно ли это верно и как я могу изменить свою логику для предотвращения этой ошибки?

Спасибо


Править: Спасибо за все большие ответы!

В конце я использовал триггеры (благодаря Vincent Malgrat, который указал на статью Tom Kyte).


Править: В РЕАЛЬНОМ КОНЦЕ я использовал хранимые процедуры и удалил триггеры :)

6
задан APC 17 November 2011 в 13:06
поделиться

8 ответов

Для тех, кто заинтересован в практическом решении этой проблемы, я решил ее, путь:

type
  TTypeOfData = (todABC, todDEF, todGHI);

  TMySerializableClass = class
  private
    FType: TTypeOfData;
  public
    property &Type: TTypeOfData read FType write FType;
    class function TypeOfDataAsString(&Type: TTypeOfData): String;
  end;

implementation

class function TMySerializableClass.TypeOfDataAsString(&Type: TTypeOfData): String;
const
  TYPE_STRING: array[TypeOfDataAsString] of String = ('ABC', 'DEF', 'GHI);
begin
  Result := TYPE_STRING[&Type];
end;

А позже, в коде сериализации, я использую RTTI, чтобы найти классовую функцию с именем AsString и вызвать ее со свойством

procedure Serialize(const V: TValue);
var
  N: String;
  T: TRttiType;
  F: TRttiField;
  M: TRttiMethod;
  R: TValue;
 begin
   case V.TypeInfo^.Kind of
   tkEnumeration:
   begin
     T := Ctx.GetType(TypeInfo(TMySerializableClass));
     N := V.TypeInfo.Name + 'AsString';
     if N[1] = 'T' then
       Delete(N, 1, 1);
     M := T.GetMethod(N);
     if (M <> nil) and M.IsClassMethod and (M.MethodKind = mkClassFunction) and (M.ReturnType.TypeKind = tkUString) then
     begin
       R := M.Invoke(TTicket, [V]);
       // serialize R.AsString
     end;
   end;
   ...
 end;
-121--1884448-

Вам нужно прочитать много ответов, но я думаю, что могу сохранить эту краткость.

«Двоичный код» относится к битам, которые поступают через схемы микропроцессора. Микропроцессор загружает каждую команду из памяти в последовательность, делая все, что они говорят. Различные семейства процессоров имеют разные форматы инструкций: x86, ARM, PowerPC и т. д. Вы указываете процессору на нужную инструкцию, давая ему адрес инструкции в памяти, а затем он весело протягивается по остальной части программы.

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

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

Компиляторы и ассемблеры C обычно настраиваются для создания статических файлов библиотеки. Для встраиваемых приложений более вероятно найти компилятор, который создает что-то вроде необработанного образа памяти с инструкциями, начинающимися с нулевого адреса. В противном случае можно написать компоновщик, который преобразует выходные данные компилятора Си в все, что угодно.

-121--1099921-

Как вы заметили, будет трудно ответить на бизнес-требования с помощью триггеров. Причина в том, что Oracle может обновлять/вставлять таблицы с несколькими потоками одновременно для одного запроса (параллельный DML). Это означает, что сеанс не может запросить таблицу, которую он обновляет , пока выполняется обновление .

Если вы действительно хотите сделать это с триггерами, вам придется следовать логике, показанной в этой статье Тома Кайта . Как видите, это не что-то простое.

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

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

12
ответ дан 8 December 2019 в 03:26
поделиться

Если вы собираетесь работать с одним и тем же файлом таким же образом (в поисках текста в определенной строке) вы можете указать свой файл. Номер линии -> Смещение.

-121--2556882-

Вы должны ИМХО не использовать триггеры для сложной бизнес-логики. Переместите его в хранимую ProC (PL / SQL пакет) или клиентский код. Приложения с большим количеством триггеров становятся неизбенными Bubouse, вы очень скоро потеряете какое-либо чувство «последовательности действий».

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

Читайте: http://www.orcle.com/technetwork/issue-archive/2008/08-sep/o58asktom-101055.html

Здесь вы можете прочитать, как вы можете решить проблему, когда вы Хотите использовать триггеры без использования автономных транзакций: http://www.procaseconsulting.com/learning/papers/200004-mutating-table.pdf

5
ответ дан 8 December 2019 в 03:26
поделиться
[11446473-

Можете ли вы ревертировать решение, включающее представления для выполнения расчета?

CREATE VIEW a_view AS
SELECT a.Id, min(b.State) State FROM tableA,tableB
WHERE a.Id=b.tableAId
GROUP BY a.Id;

Я согласен, что хранимые процессы (как указано здесь на других постах), также являются хорошим кандидатом - но обратите внимание, что вид Будет автоматически обновляться, в то время как я полагаю, вам придется запланировать запуск хранимых - procs, чтобы сохранить данные «In-Sync»: что может быть хорошо - это зависит от ваших требований.

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

3
ответ дан 8 December 2019 в 03:26
поделиться

Вы можете использовать как триггеры, так и ограничения целостности для определения и Обеспечить соблюдение любого типа правила целостности. Тем не менее, Oracle Corporation сильно рекомендует использовать триггеры к ограничивать данные ввода только в Следующие ситуации:

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

  • Не NULL, уникальный
  • Первичный ключ
  • Внешний ключ
  • Проверка
  • Удалить Cascade
  • Удалить Set Null

Источник: Концепции базы данных Oracle9i

2
ответ дан 8 December 2019 в 03:26
поделиться

Делая вещи, подобные этому, - это отличное искушение, и если вы будете следовать предложениям в статье Тома Китай, на которые ссылается другие, это возможно. Однако только потому, что что-то может быть сделано , не означает, что он должен быть сделан . Я настоятельно рекомендую вам реализовать что-то вроде этого как сохраненную процедуру / функцию / пакет. Комплексная логика такого рода не должна выполняться с использованием триггеров, несмотря на очевидные искушения, потому что она значительно повышает сложность системы без соответствующего увеличения утилиты. Я должен работать на код, как это иногда, и это не радость.

Удачи.

1
ответ дан 8 December 2019 в 03:26
поделиться

Не используйте автономные транзакции, или вы получите очень интересных результатов.

Чтобы избежать проблемы с мутационными таблицами, вы можете сделать следующее:

в A после вставки или обновления или удаления для каждого триггера строки, узнайте родительский идентификатор и сохранить его в коллекции PL / SQL (внутри пакета ). Затем, в после вставки или обновлении или удалении триггера (уровень оператора, без детали «Для каждой строки») прочитайте родительские идентификаторы из коллекции PL / SQL и обновите родительский стол соответственно.

1
ответ дан 8 December 2019 в 03:26
поделиться

В качестве примера того, почему ваша логика не работает, возьмем сценарий, в котором в PARENT A есть запись 1 с записями CHILD 1A и 1B. СОСТОЯНИЕ 1A равно 10, а 1B равно 15, поэтому вы хотите, чтобы вашему родителю было 10.

Теперь кто-нибудь обновляет СОСТОЯНИЕ 1A до 20, в то же время, кто-нибудь удаляет 1B. Поскольку удаление 1B не завершено, транзакция, обновляющая 1A, все равно будет видеть 1B и захочет установить состояние родителя равным 15, в то время как транзакция, удаляющая 1B, будет видеть старое не завершенное значение 1A и захочет, чтобы состояние родителя было 10.

Если вы де-нормализуете это, вы должны быть очень осторожны с блокировкой, чтобы ПЕРЕД вставкой/обновлением/удалением любых дочерних записей, родительская запись была заблокирована, выполнили свои изменения, выбрали все дочерние записи, обновили родительскую запись, а затем зафиксировали снятие блокировок. Хотя это можно сделать с помощью триггеров, лучше всего использовать сохраненную процедуру.

2
ответ дан 8 December 2019 в 03:26
поделиться

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

Не знаю, где вы это видели, но я знаю, что уже много раз выкладывал это мнение.

Почему я считаю, что мутирующие таблицы обычно указывают на изъян в модели данных? Потому что тот тип "требования", который приводит в движение код, который бросает ORA-4091, часто ассоциируется с плохой конструкцией, особенно с недостаточной нормализацией.

Классическим примером этого является ваш сценарий. Вы получаете ORA-04091, потому что выбираете из TableC, когда вставляете или обновляете его. Но почему вы выбираете из TableC? Потому что вам "нужно" обновить столбец в его родителе, TableB. Но этот столбец - избыточная информация. В полностью нормализованной модели данных этот столбец не будет существовать.

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

Итак, как можно изменить логику? Простой ответ - опустить столбцы и не беспокоиться о хранении наименьшего состояния по родительскому идентификатору. Вместо этого, выполняйте запрос MIN() всякий раз, когда вам нужна эта информация. Если вы часто нуждаетесь в этом и выполнение запроса будет дорогостоящим, то вы строите материализованные представления, в которых хранятся данные (обязательно используйте ENABLE QUERY REWRITE)

.
3
ответ дан 8 December 2019 в 03:26
поделиться
Другие вопросы по тегам:

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