Oracle sql triggers :old :new обращение по заранее не известному полю

uaha1979

Пишу тригер по типу "BEFORE INSERT OR UPDATE OR DELETE", там есть в псевдозаписи : old :new, я могу обращаться к полям по имени например : old.id.
Вопрос: могу ли я обращаться по полю, которое не известно к моменту компиляции?
Например:
вместо того чтобы писать

save_to_history('column1', :old.column1, :new.column1);
save_to_history('column2', :old.column2, :new.column2);
save_to_history('column3', :old.column3, :new.column3);
save_to_history('column4', :old.column4, :new.column4);
save_to_history('column5', :old.column5, :new.column5);
итд 50 строк

Написать нечто вроде:

foreach(Column the_column in :old.Columns){
save_to_history(the_column.ToString :old.the_column, ;new.the_column);
}

функция save_to_history просто добавляет строки (имя поля, старое значение, новое значение) в базу данных.

kill-still

а не проще ли переделать процедуру save_to_history, чтобы она рекорды принимала? =/

0000

Вопроса не понял, но может execute immediate поможет?

kill-still

у него свой контекст. в пл/сиквеле нет замыканий. передать переменные в контекст можно, но для этого нужно к ним по имени обращаться же, а ТС от этого и хочет избавиться. ^_^

0000

Это я то же не понял.

mbolik1

Не уверен что это поможет, но можно попробовать.
Пишешь compaund trigger который
after each row сохраняет id изменяемой записи во внутренний массив.
after statement делает селект из таблицы по массивы сохранённых id и пишет куда-то в историю.

uaha1979

Еще пример:
На вход: имя таблицы,
На выход: таблица следующего вида

TableName FieldName Value Row
-------------------------------------------------


По сути таблица для транспортирования данных

Реализация на ABAP языке.
На вход tableName.

Определения переменных опущены.

field-symbols: <val> type any
,<table> type any table
,<line> type any
,<text> type any
.
select * "берем имена всех колонок для таблицы
from dd03l
into table fldName
where tabname = tabName
.
create data commonTableRef type table of (tabName).
assign commonTableRef->* to <table>. "создаем табличку заданного типу

select * "заполняем ее
from (tabName)
into table <table>
.
row = 1.
loop at <table> assigning <line>. "идем по таблице
loop at fldName into fldNameLine. "идем по колонкам
clear: dotLine.
assign component fldNameLine-fieldName of structure <line> to <text>. "АНАЛОГ ЭТОГО Я ИЩУ В PL/SQL
dotLine-id = dotId. "Заполняем все нужные поля выходной таблицы
dotLine-table = tabName.
dotLine-field = fldNameLine-fieldName.
dotLine-value = <text>.
append dotLine to dot.
endloop.
row = row + 1.
endloop.

По поводу моего первого поста:
Я знаю имя таблицы.
По нему я могу получить имена всех ее колонок(столбцов).
Далее я хочу сделать цикл по всем именам колонок (переменная forVariable):
Дай мне из :new то, что лежит в колонке с именем forVariable

kill-still

сдаётся мне, тут классический случай: http://www.gunsmoker.ru/2008/10/x-y-z.html

0000

Ты исходную задачу можешь сформулировать? Что-то у меня подозрение, что новый велосипед рождается.
Тебе надо просто отслеживать изменения в таблице, и, когда меняется значение в некотором столбце писать в лог
<имя таблицы> <имя столбцы> <старое> <новое>
Или же
<имя таблицы> <список всех полей + старые значения> <список всех полей + новые значения>
Или речь вообще не о логировании?

uaha1979

сдаётся мне, тут классический случай
sad but true
Что-то у меня подозрение, что новый велосипед рождается.
У меня тоже
Тебе надо просто отслеживать изменения в таблице, и, когда меняется значение в некотором столбце писать в лог
<имя таблицы> <имя столбцы> <старое> <новое>
именно так

kill-still

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

mbolik1

Это конечно не правда. Например, Том Кайт.
Проблема в том что :new и \:old это не record, это псевдозапись.

uaha1979

В соответствии с рекомендацией переформулирую вопрос:
Как отслеживать изменения в таблице? и, когда меняется значение в некотором столбце писать в лог:
<имя таблицы> <имя столбца> <старое> <новое>?
Решения которые я обдумывал:
1. навесить на таблицу обычный тригер, в этом случае я не знаю как обратиться к :new.(Какое-то значение, заранее не известное)
2. Сделать составной тригер, во время обработки строки (before/after for each row кидать во внутренний массив id,
после обработки строк, делать select из таблицы по заданным id, кидать эту запись в лог2 (в формате <имя таблицы> <имя столбца> <новое значение> ).
При случае нужный формат (<имя таблицы> <имя столбца> <старое> <новое>) я смогу восстановить.
Я так понимаю, что во втором варианте я смогу реализовать формат лог2 ( <имя таблицы> <имя столбца> <новое значение> тк в этот момент я могу делать select из измененной таблицы и не имею дел с псевдозаписями?

mbolik1

1. навесить на таблицу обычный тригер, в этом случае я не знаю как обратиться к :new.(Какое-то значение, заранее не известное)
Это просто не возможно.
Я так понимаю, что во втором варианте я смогу реализовать формат лог2 ( <имя таблицы> <имя столбца> <новое значение> тк в этот момент я могу делать select из измененной таблицы и не имею дел с псевдозаписями?
Я не уверен, но может удастся получить и старые значения вызвав процедуру с pragma AUTONOMOUS_TRANSACTION.
Но что-то мне кажется гораздо проще написать ddl-триггер который будет переписывать dml-триггер, если структура таблицы изменяется.

uaha1979

Но что-то мне кажется гораздо проще написать ddl-триггер который будет переписывать dml-триггер, если структура таблицы изменяется.
Спасибо, так и реализую.
Спасибо всем кто отписался =)
Оставить комментарий
Имя или ник:
Комментарий: