Во-первых, в настоящее время у нас есть желаемое поведение, но его нетривиально поддерживать, когда необходимы какие-либо изменения в базе данных. Я ищу что-нибудь более простое, более эффективное или более простое в обслуживании (все, что делает что-либо из этих 3, было бы очень кстати). Когда мы выполняем обновление, создается строка истории, которая является копией строки current , и значения текущей строки обновляются. В результате мы имеем запись истории того, как была строка до ее обновления.
Обоснование: мы должны соблюдать ряд федеральных правил и пошли по этому пути, чтобы иметь полную историю аудита всего, а также мы можем посмотреть на базу данных в любой момент времени и посмотреть, как все выглядело ( будущее требование). По тем же причинам я не могу изменить способ записи истории ... любое решение должно приводить к тем же данным, что и текущие триггеры.
Вот как выглядят текущие триггеры для таблицы Contact
:
(для краткости удалены бесполезные поля, количество полей не имеет значения)
Перед обновлением (каждая строка):
DECLARE
indexnb number;
BEGIN
:new.date_modified := '31-DEC-9999';
indexnb := STATE_PKG.newCONTACTRows.count + 1;
:new.date_start := sysdate;
:new.version := :old.version + 1;
state_pkg.newCONTACTRows(indexnb).ID := :old.ID;
state_pkg.newCONTACTRows(indexnb).PREFIX := :old.PREFIX;
state_pkg.newCONTACTRows(indexnb).FIRST_NAME := :old.FIRST_NAME;
state_pkg.newCONTACTRows(indexnb).MIDDLE_NAME := :old.MIDDLE_NAME;
state_pkg.newCONTACTRows(indexnb).LAST_NAME := :old.LAST_NAME;
--Audit columns after this
state_pkg.newCONTACTRows(indexnb).OWNER := :old.OWNER;
state_pkg.newCONTACTRows(indexnb).LAST_USER := :old.LAST_USER;
state_pkg.newCONTACTRows(indexnb).DATE_CREATED := :old.DATE_CREATED;
state_pkg.newCONTACTRows(indexnb).DATE_MODIFIED := sysdate;
state_pkg.newCONTACTRows(indexnb).VERSION := :old.VERSION;
state_pkg.newCONTACTRows(indexnb).ENTITY_ID := :old.id;
state_pkg.newCONTACTRows(indexnb).RECORD_STATUS := :old.RECORD_STATUS;
state_pkg.newCONTACTRows(indexnb).DATE_START := :old.DATE_START;
END;
Перед обновлением (один раз для всех строк):
BEGIN
state_pkg.newCONTACTRows := state_pkg.eCONTACTRows;
END;
После обновления (один раз для всех строк):
DECLARE
BEGIN
for i in 1 .. STATE_PKG.newCONTACTRows.COUNT loop
INSERT INTO "CONTACT" (
ID,
PREFIX,
FIRST_NAME,
MIDDLE_NAME,
LAST_NAME,
OWNER,
LAST_USER,
DATE_CREATED,
DATE_MODIFIED,
VERSION,
ENTITY_ID,
RECORD_STATUS,
DATE_START)
VALUES (
CONTACT_SEQ.NEXTVAL,
state_pkg.newCONTACTRows(i).PREFIX,
state_pkg.newCONTACTRows(i).FIRST_NAME,
state_pkg.newCONTACTRows(i).MIDDLE_NAME,
state_pkg.newCONTACTRows(i).LAST_NAME,
state_pkg.newCONTACTRows(i).OWNER,
state_pkg.newCONTACTRows(i).LAST_USER,
state_pkg.newCONTACTRows(i).DATE_CREATED,
state_pkg.newCONTACTRows(i).DATE_MODIFIED,
state_pkg.newCONTACTRows(i).VERSION,
state_pkg.newCONTACTRows(i).ENTITY_ID,
state_pkg.newCONTACTRows(i).RECORD_STATUS,
state_pkg.newCONTACTRows(i).DATE_START
);
end loop;
END;
Пакет, определенный как (обрезанный, полная версия - просто копия этого для каждой таблицы):
PACKAGE STATE_PKG IS
TYPE CONTACTArray IS TABLE OF CONTACT%ROWTYPE INDEX BY BINARY_INTEGER;
newCONTACTRows CONTACTArray;
eCONTACTRows CONTACTArray;
END;
Текущий результат
Вот пример полученной истории:
ID First Last Ver Entity_ID Date_Start Date_Modified
1196 John Smith 5 0 12/11/2009 10:20:11 PM 12/31/9999 12:00:00 AM
1201 John Smith 0 1196 12/11/2009 09:35:20 PM 12/11/2009 10:16:49 PM
1203 John Smith 1 1196 12/11/2009 10:16:49 PM 12/11/2009 10:17:07 PM
1205 John Smith 2 1196 12/11/2009 10:17:07 PM 12/11/2009 10:17:19 PM
1207 John Smith 3 1196 12/11/2009 10:17:19 PM 12/11/2009 10:20:00 PM
1209 John Smith 4 1196 12/11/2009 10:20:00 PM 12/11/2009 10:20:11 PM
Каждая запись истории имеет Entity_ID, который является идентификатором текущей строки, Date_Start для новой записи соответствует Date_Modified последней строки истории. Это позволяет нам делать запросы вроде Where Entity_ID = :id Or ID = :id And :myDate < Date_Modified And :myDate >= Date_Start
. Историю можно получить по Entity_ID = :current_id
.
Есть ли лучший подход, который, как мы надеемся, более удобен в обслуживании / гибкий для этого? Концепция проста: при обновлении строки скопируйте ее в ту же таблицу через вставку со старыми значениями, а затем обновите текущая строка ... но на самом деле, делая это, мне еще предстоит найти более простой способ. Я надеюсь, что кто-то намного хитрее / мудрее в Oracle имеет лучший подход к этому. Скорость не имеет большого значения, мы на 99% читаем 1% записей, как и большинство веб-приложений, и все массовые операции являются вставками, а не обновлениями, которые не будут создавать историю.
Если у кого-нибудь есть идеи по упрощению обслуживания, я был бы очень признателен, спасибо!