Мои таблицы:
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), так, чтобы после того, как каждое действие, это ступает, выполнилось бы:
Как я могу постараться не 'видоизменять ошибку таблицы'? Это, сохраняют для использования автономных транзакций в этом примере? Я видел некоторые мнения, та ошибка таблицы видоизменения указывает на дефекты в логике приложения - действительно ли это верно и как я могу изменить свою логику для предотвращения этой ошибки?
Спасибо
Править: Спасибо за все большие ответы!
В конце я использовал триггеры (благодаря Vincent Malgrat, который указал на статью Tom Kyte).
Править: В РЕАЛЬНОМ КОНЦЕ я использовал хранимые процедуры и удалил триггеры :)
Для тех, кто заинтересован в практическом решении этой проблемы, я решил ее, путь:
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). Это означает, что сеанс не может запросить таблицу, которую он обновляет , пока выполняется обновление .
Если вы действительно хотите сделать это с триггерами, вам придется следовать логике, показанной в этой статье Тома Кайта . Как видите, это не что-то простое.
Есть еще один, более простой, более элегантный, более простой в обслуживании метод: использовать процедуры. Отмените право обновления/вставки для пользователей приложения и запишите набор процедур, позволяющих приложению обновлять столбцы состояния.
Эти процедуры удерживали бы блокировку родительской строки (чтобы предотвратить несколько сеансов для изменения одного и того же набора строк) и применяли бы вашу бизнес-логику эффективным, читаемым и легко поддерживаемым способом.
Если вы собираетесь работать с одним и тем же файлом таким же образом (в поисках текста в определенной строке) вы можете указать свой файл. Номер линии -> Смещение.
-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
Можете ли вы ревертировать решение, включающее представления для выполнения расчета?
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»: что может быть хорошо - это зависит от ваших требований.
Я думаю, что другой вариант состоит в том, чтобы создать некоторые функции для выполнения расчета, но лично я бы выбрал на просмотр-подход (все равно ровно).
Вы можете использовать как триггеры, так и ограничения целостности для определения и Обеспечить соблюдение любого типа правила целостности. Тем не менее, Oracle Corporation сильно рекомендует использовать триггеры к ограничивать данные ввода только в Следующие ситуации:
для обеспечения соблюдения ссылочной целостности, когда Ребенок и родительские таблицы находятся на Различные узлы распределенного база данных для обеспечения соблюдения сложного бизнеса Правила не определены с использованием целостности ограничения при необходимости Референтное правило целостности не может быть принудительно используя следующую целостность Ограничения:
- Не NULL, уникальный
- Первичный ключ
- Внешний ключ
- Проверка
- Удалить Cascade
- Удалить Set Null
Делая вещи, подобные этому, - это отличное искушение, и если вы будете следовать предложениям в статье Тома Китай, на которые ссылается другие, это возможно. Однако только потому, что что-то может быть сделано , не означает, что он должен быть сделан . Я настоятельно рекомендую вам реализовать что-то вроде этого как сохраненную процедуру / функцию / пакет. Комплексная логика такого рода не должна выполняться с использованием триггеров, несмотря на очевидные искушения, потому что она значительно повышает сложность системы без соответствующего увеличения утилиты. Я должен работать на код, как это иногда, и это не радость.
Удачи.
Не используйте автономные транзакции, или вы получите очень интересных результатов.
Чтобы избежать проблемы с мутационными таблицами, вы можете сделать следующее:
в A после вставки или обновления или удаления для каждого триггера строки, узнайте родительский идентификатор и сохранить его в коллекции PL / SQL (внутри пакета ). Затем, в после вставки или обновлении или удалении триггера (уровень оператора, без детали «Для каждой строки») прочитайте родительские идентификаторы из коллекции PL / SQL и обновите родительский стол соответственно.
В качестве примера того, почему ваша логика не работает, возьмем сценарий, в котором в PARENT A есть запись 1 с записями CHILD 1A и 1B. СОСТОЯНИЕ 1A равно 10, а 1B равно 15, поэтому вы хотите, чтобы вашему родителю было 10.
Теперь кто-нибудь обновляет СОСТОЯНИЕ 1A до 20, в то же время, кто-нибудь удаляет 1B. Поскольку удаление 1B не завершено, транзакция, обновляющая 1A, все равно будет видеть 1B и захочет установить состояние родителя равным 15, в то время как транзакция, удаляющая 1B, будет видеть старое не завершенное значение 1A и захочет, чтобы состояние родителя было 10.
Если вы де-нормализуете это, вы должны быть очень осторожны с блокировкой, чтобы ПЕРЕД вставкой/обновлением/удалением любых дочерних записей, родительская запись была заблокирована, выполнили свои изменения, выбрали все дочерние записи, обновили родительскую запись, а затем зафиксировали снятие блокировок. Хотя это можно сделать с помощью триггеров, лучше всего использовать сохраненную процедуру.
Я видел некоторые мнения, что ошибка мутирующей таблицы указывает на недостатки в логике
. приложение - так ли это и как я могу изменить свою логику, чтобы предотвратить это
. Ошибка?
Не знаю, где вы это видели, но я знаю, что уже много раз выкладывал это мнение.
Почему я считаю, что мутирующие таблицы обычно указывают на изъян в модели данных? Потому что тот тип "требования", который приводит в движение код, который бросает ORA-4091, часто ассоциируется с плохой конструкцией, особенно с недостаточной нормализацией.
Классическим примером этого является ваш сценарий. Вы получаете ORA-04091, потому что выбираете из TableC
, когда вставляете или обновляете его. Но почему вы выбираете из TableC
? Потому что вам "нужно" обновить столбец в его родителе, TableB
. Но этот столбец - избыточная информация. В полностью нормализованной модели данных этот столбец не будет существовать.
Денормализация часто упоминается как механизм повышения производительности запросов. К сожалению, сторонники денормализации лоскаются над ее стоимостью, которая оплачивается в валюте излишней сложности при вставке, обновлении и удалении.
Итак, как можно изменить логику? Простой ответ - опустить столбцы и не беспокоиться о хранении наименьшего состояния по родительскому идентификатору. Вместо этого, выполняйте запрос MIN()
всякий раз, когда вам нужна эта информация. Если вы часто нуждаетесь в этом и выполнение запроса будет дорогостоящим, то вы строите материализованные представления, в которых хранятся данные (обязательно используйте ENABLE QUERY REWRITE
)