powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Поиск измененных полей при обновлении строки в триггере
25 сообщений из 28, страница 1 из 2
Поиск измененных полей при обновлении строки в триггере
    #38571120
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте. Нужно написать триггеры, на update которые фиксировали бы в каких полях произошли изменения. Например есть таблица Clients в ней поля name, phone, address, e-mail,... изменения произошли в двух полях name, phone. Нужно, что в триггере в итоге сформировалась строка типа ch_str = 'name, phone';
Самый простой вариант это:
ch_str = '';
if (old.name <> new.name) then
ch_str = ch_str || ', name';

if (old.phone <> new.phone) then
ch_str = ch_str || ', phone';

но мне нужно поля динамически получать, это можно сделать таким запросом:
select trim(rdb$field_name)
from rdb$relation_fields
where rdb$relation_name = 'CLIENTS'
into :f_name

Вся проблема в том, что не знаю как получить значения new.f_name и old.f_name.
Подскажите, возможно ли такое сделать, если да то как или где поискать, может еще какой-нибудь способ есть решить данную задачу.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571137
Гхостик
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
lamborgine,

Единственное что вижу - генерировать код триггера при изменении структуры таблицы. Однако, автоматически вызывать такую генерацию (и компиляцию) неоткуда, только ручками не забывать вызывать.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571168
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Понятно, что в ручную можно, но хотелось бы универсальности. Таблиц около 500 и для каждой делать триггер долго, да и большая вероятность, что кто-нибудь добавит новое поле, а триггер не подредактирует.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571171
Фотография CyberMax
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
lamborgine,

Напиши обработку в IBExpert.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571175
Фотография Exteris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Наверняка можно как-нибудь через execute statement+execute block извратиться.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571182
pastor
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ExterisНаверняка можно как-нибудь через execute statement+execute block извратиться.

ага. в триггере на подключение.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571189
m7m
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
lamborgine,

Брось эту затею, в смысле получать поля динамически
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571232
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
CyberMaxlamborgine,

Напиши обработку в IBExpert.

Можно по подробней..

ExterisНаверняка можно как-нибудь через execute statement+execute block извратиться.

Пробовал, не получилось. Если есть идеи как, напиши.

ExterisБрось эту затею, в смысле получать поля динамически.

Если не нужно было, не начинал =)
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571342
Фотография Exteris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Что ты потом будешь с этим списком полей делать?
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571347
dennis-r
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
lamborgine
ExterisНаверняка можно как-нибудь через execute statement+execute block извратиться.

Пробовал, не получилось. Если есть идеи как, напиши.
Печка , от которой можно начинать плясать.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571384
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ExterisЧто ты потом будешь с этим списком полей делать?
Мне это нужно для логов работы с таблицами.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571394
Фотография Симонов Денис
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: plsql
1.
if (old.name <> new.name) then


правильней
Код: plsql
1.
if (old.name is distinct from new.name) then


а то изменения NULL -> value и value -> NULL пропустишь
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571400
Фотография Exteris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как вариант:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
CREATE OR ALTER TRIGGER TEST_BU0 FOR TEST
ACTIVE BEFORE UPDATE POSITION 0
AS
declare variable ch_str varchar(255);
declare variable f_name varchar(32);
declare variable ch integer;
begin
  for select rdb$field_name
  from rdb$relation_fields
  where rdb$relation_name = 'TEST'
  into f_name do begin
     execute statement 'select iif(new.'||f_name||'<>old.'||f_name||',1,0) from rdb$database' into :ch;
     if (ch=1) then ch_str=ch_str||f_name||',';
  end
end


Надеюсь массовых изменений не планируется?
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571401
Фотография Exteris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
И да, лучше is distinct.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571402
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Симонов Денис
Код: plsql
1.
if (old.name <> new.name) then


правильней
Код: plsql
1.
if (old.name is distinct from new.name) then


а то изменения NULL -> value и value -> NULL пропустишь
Спасибо, учту.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571405
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ExterisНадеюсь массовых изменений не планируется?
Массовых нет, но периодические да.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571500
Гхостик
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
lamborgineПонятно, что в ручную можно, но хотелось бы универсальности. Таблиц около 500 и для каждой делать триггер долгоТы не понял слово "генерировать"? Это значит "формировать программно".

2 Exteris
Попробовал, не работает. Да и понятно - откуда sql-ю знать переменную из psql?

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
CREATE TABLE TEST1 (
    ID  INTEGER
);

SET TERM ^ ;
/* Trigger: TEST1_AI */
CREATE OR ALTER TRIGGER TEST1_AI FOR TEST1
ACTIVE AFTER INSERT POSITION 0
as
declare variable v integer;
begin
  execute statement 'select new.id from rdb$database' into :v;
end
^

SET TERM ; ^


И Column does not belong to referenced table.
Dynamic SQL Error.
SQL error code = -206.
Column unknown.
NEW.ID.
при попытке вставки
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571538
Фотография Exteris
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Гхостик2 Exteris
Попробовал, не работает. Да и понятно - откуда sql-ю знать переменную из psql?

Точно же.(
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571564
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
lamborgineМассовых нет, но периодические да.
Переходи на тройку, там есть DDL триггера, сможешь генерировать новый текст триггера при
изменении структуры таблицы автоматически. Или просто проверяй в триггере число полей
таблицы и если больше ожидаемого - выкидывай исключение, чтобы тот, кто таблицу расширил,
а про протоколирование забыл, исправил свою ошибку.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571579
lamborgine
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dimitry Sibiryakov
По ходу так и придется делать..
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571584
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну или можешь контролировать скрипт изменения БД каждого балбеса перед применением. Забыть
добавить поле в триггер - не самая худшая ошибка, которую они могут допустить...
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571742
Таблоид
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
lamborgineПо ходу так и придется делать..На продакшен пока рановато тройку ставить.
Создайте на его копии табличку
Код: sql
1.
create table audit_table(tab_name varchar(31), fld_name varchar(31), old_value varchar(NNNN), new_value varchar(NNNN), old_blob blob, new_blob blob)

где NNNN - максимально длинное поле из ваших варчаров. В примере ниже NNNN = 2048.
С блобами, конечно, проблема: там и 2 Гб может встретиться, поэтому их надо записывать в свою пару полей (old_ & new_blob).
Далее натравите на вашу базу вот этот запрос:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
set term ^;
execute block returns(trg_text varchar(150)) as
declare our_max_char int = 2048; -- suppose as maximal length of any (var)char field in our database
declare tab_name varchar(31);
declare fld_name varchar(31);
declare is_blob int;
declare trg_prefix varchar(2)='t$';
declare tab_start varchar(31) = '';
declare fld_cnt int;
declare fld_idx int = 0;
begin
  for
    with
    c as(
        select rf.rdb$relation_name tab_name, rf.rdb$field_name fld_name, iif(ff.rdb$field_type=261,1,0) is_blob
        from rdb$relation_fields rf
        join rdb$fields ff on rf.rdb$field_source = ff.rdb$field_name
        left join (
            select rc.rdb$relation_name tab_name, rs.rdb$field_name fld_name
            from rdb$relation_constraints rc
            join rdb$index_segments rs on rc.rdb$index_name = rs.rdb$index_name
            where rc.rdb$constraint_type='PRIMARY KEY'
        ) pk on rf.rdb$relation_name=pk.tab_name and  rf.rdb$field_name = pk.fld_name
        where pk.tab_name is null -- exclude fields that forms primary key in tables
              and rf.rdb$system_flag =0
              and ff.rdb$computed_source is null
    )
    select d.tab_name,d.fld_cnt,c.fld_name, c.is_blob
    from(
        select c.tab_name,count(c.fld_name) fld_cnt -- need for stop generating trigger text after last field
        from c
        group by c.tab_name
    ) d
    join c on d.tab_name=c.tab_name
    order by tab_name,fld_name
  into
    tab_name, fld_cnt, fld_name, is_blob
  do
  begin
    if (fld_idx=0) then --  (tab_name <> tab_start) then
    begin
        tab_start = tab_name;
        trg_text='set term ^;'; suspend;
        trg_text='create or alter trigger '||trg_prefix||trim(left(lower(tab_name),31-char_length(trg_prefix)))
                 ||' for '||trim(lower(tab_name)) || ' active after insert or update position 32760 as'; suspend;
        trg_text='begin'; suspend;
        trg_text='  --- ### GENERATED AUTO! DO NOT EDIT! ###'; suspend;
        trg_text='  --- ### COMPUTED AND PRIMARY KEY FIELDS EXCLUDED ###'; suspend;
    end

    trg_text='  if (old.'||trim(lower(fld_name))||' is distinct from new.'||trim(lower(fld_name))||') then '; suspend;
    if (is_blob=0) then
    begin
        trg_text='    insert into audit_table(tab_name, fld_name, old_value, new_value)'; suspend;
        trg_text='    values('''||trim(lower(tab_name))||''''
                          ||','''||trim(lower(fld_name))||''','
                          ||' cast(old.'||trim(lower(fld_name))||' as varchar('||our_max_char||')),'
                          ||' cast(new.'||trim(lower(fld_name))||' as varchar('||our_max_char||'))'
                          ||');';
    end
    else begin
        trg_text='    insert into audit_table(tab_name, fld_name, old_blob, new_blob)'; suspend;
        trg_text='    values('''||trim(lower(tab_name))||''''
                          ||','''||trim(lower(fld_name))||''','
                          ||' old.'||trim(lower(fld_name))||','
                          ||' new.'||trim(lower(fld_name))
                          ||');';
    end
    suspend;
    fld_idx=fld_idx+1;
    if (fld_idx=fld_cnt) then
    begin
        trg_text='end^'; suspend;
        trg_text='set term ;^;'; suspend;
        trg_text='commit;'; suspend;
        fld_idx=0;
    end
  end
end^
set term ;^
commit;

- и посмотрите результат. Это будет скрипт для генерации таких триггеров, только без обработки вычисляемых полей и тех полей, которые участвуют в формировании PK (последние отслеживать в нормальной системе нет смысла).
Например, для таблиц:
Код: sql
1.
2.
recreate table tblob(x int primary key, b blob);
recreate table tcalc(x int, y int, z computed by(x+y));

- будет вот это:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
set term ^;
create or alter trigger t$tblob for tblob active after insert or update position 32760 as
begin
  --- ### GENERATED AUTO! DO NOT EDIT! ###
  --- ### COMPUTED AND PRIMARY KEY FIELDS EXCLUDED ###
  if (old.b is distinct from new.b) then
    insert into audit_table(tab_name, fld_name, old_blob, new_blob)
    values('tblob','b', old.b, new.b);
end^
set term ;^;
commit;
set term ^;
create or alter trigger t$tcalc for tcalc active after insert or update position 32760 as
begin
  --- ### GENERATED AUTO! DO NOT EDIT! ###
  --- ### COMPUTED AND PRIMARY KEY FIELDS EXCLUDED ###
  if (old.x is distinct from new.x) then
    insert into audit_table(tab_name, fld_name, old_value, new_value)
    values('tcalc','x', cast(old.x as varchar(2048)), cast(new.x as varchar(2048)));
  if (old.y is distinct from new.y) then
    insert into audit_table(tab_name, fld_name, old_value, new_value)
    values('tcalc','y', cast(old.y as varchar(2048)), cast(new.y as varchar(2048)));
end^
set term ;^;
commit;
Сохраните его результат в виде .sql файлика "tmp_gen_triggers.sql":
Код: plaintext
isql server_address/port:your_prod_alias -i <этот запрос> 1>tmp_gen_triggers.sql 2>&1
- и натравливайте затем периодически на свою базу, когда будет нужда:
Код: plaintext
isql server_address/port:your_prod_alias -i tmp_gen_triggers.sql -m -o tmp_gen_triggers.err
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571766
dennis-r
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
lamborgine,

Автоматическое (пере)создания триггеров журналирования обновлений
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
execute block
returns(TRIG_CREATE_SCRIPT varchar(4000))
as
declare variable TABLE_NAME varchar(31);
declare variable FIELD_NAME varchar(31);
declare variable TRIG_POS integer;
declare variable CRLF varchar(2);
begin

  CRLF = ascii_char(13) || ascii_char(10);

  TRIG_POS = 99; -- Позиция для триггера журналирования

  for select trim(RR.RDB$RELATION_NAME)
      from RDB$RELATIONS RR
      where (RR.RDB$VIEW_BLR is null) and (RR.RDB$SYSTEM_FLAG = 0)
      into :TABLE_NAME
  do begin

    TRIG_CREATE_SCRIPT =
      'create or alter trigger ' || TABLE_NAME || '_BU'  || TRIG_POS || ' for ' || TABLE_NAME || CRLF ||
      'active before update position ' || TRIG_POS || CRLF ||
      'as' || CRLF ||
      'declare variable CH_FLD_LIST varchar(1000);' || CRLF ||
      'begin' || CRLF || CRLF ||
      ' CH_FLD_LIST = ''''' || CRLF;

    for select trim(rdb$field_name)
        from rdb$relation_fields
        where rdb$relation_name = :TABLE_NAME
        into :FIELD_NAME
    do
      TRIG_CREATE_SCRIPT = TRIG_CREATE_SCRIPT ||
        '  if old.' || FIELD_NAME || ' <> new.' || FIELD_NAME || ' then' || CRLF ||
        '    CH_FLD_LIST = CH_FLD_LIST || ''' || FIELD_NAME || ', '';' || CRLF;

   TRIG_CREATE_SCRIPT = TRIG_CREATE_SCRIPT ||
     '  CH_FLD_LIST = substring(CH_FLD_LIST from 1 for char_length(CH_FLD_LIST) - 2);' ||
     CRLF || CRLF;

    -- Вместо *** добавить код, который полученный список изменённых полей применяет по назначению
    --TRIG_CREATE_SCRIPT = TRIG_CREATE_SCRIPT || '  ***' || CRLF;

    TRIG_CREATE_SCRIPT = TRIG_CREATE_SCRIPT || CRLF || 'end';

    /* Выводим текст триггеров в таблицу */
    suspend;
    /* И/или сразу меняем */
    -- execute statement TRIG_CREATE_SCRIPT;
   /* Раскоментируй нужное! */ 

  end

end

Пример созданного триггера
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
create or alter trigger T_CMN_PERSONS_BU99 for T_CMN_PERSONS
active before update position 99
as
declare variable CH_FLD_LIST varchar(1000);
begin

 CH_FLD_LIST = ''
  if old.ID <> new.ID then
    CH_FLD_LIST = CH_FLD_LIST || 'ID, ';
  if old.FIRST_NAME <> new.FIRST_NAME then
    CH_FLD_LIST = CH_FLD_LIST || 'FIRST_NAME, ';
  if old.MIDDLE_NAME <> new.MIDDLE_NAME then
    CH_FLD_LIST = CH_FLD_LIST || 'MIDDLE_NAME, ';
  if old.LAST_NAME <> new.LAST_NAME then
    CH_FLD_LIST = CH_FLD_LIST || 'LAST_NAME, ';
  if old.BRIEF_NAME <> new.BRIEF_NAME then
    CH_FLD_LIST = CH_FLD_LIST || 'BRIEF_NAME, ';
  if old.FULL_NAME <> new.FULL_NAME then
    CH_FLD_LIST = CH_FLD_LIST || 'FULL_NAME, ';
  if old.SEX <> new.SEX then
    CH_FLD_LIST = CH_FLD_LIST || 'SEX, ';
  if old.BIRTHDAY <> new.BIRTHDAY then
    CH_FLD_LIST = CH_FLD_LIST || 'BIRTHDAY, ';
  if old.OBJECT_VIEW <> new.OBJECT_VIEW then
    CH_FLD_LIST = CH_FLD_LIST || 'OBJECT_VIEW, ';
  if old.NOTE <> new.NOTE then
    CH_FLD_LIST = CH_FLD_LIST || 'NOTE, ';
  if old.EVENT_ID <> new.EVENT_ID then
    CH_FLD_LIST = CH_FLD_LIST || 'EVENT_ID, ';
  CH_FLD_LIST = substring(CH_FLD_LIST from 1 for char_length(CH_FLD_LIST) - 2);

  ***

end



NB:
0. Никаких гарантий не даю - отладка на совести использующего.
1. В тексте шаблона необходимо добавить код, который делает что-нибудь полезное со полученной строкой.
2. Ну, и не забывать запускать после каждого изменения набора полей таблиц
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571771
dennis-r
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Таблоид,

От блин, опередил с комбайном. Да ещё движок помощнее моего будет.
...
Рейтинг: 0 / 0
Поиск измененных полей при обновлении строки в триггере
    #38571819
Таблоид
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dennis-rДа ещё движок помощнее моего будет. там надо бы допилить определение NNNN - максимального размера для варчаров. Ибо запихивать в varchar(32760) строки, если их длина никогда не превосходит 80, не только "нет смысла", а вообще - плохо. Скажется затем на выборках из этого аудита.
...
Рейтинг: 0 / 0
25 сообщений из 28, страница 1 из 2
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Поиск измененных полей при обновлении строки в триггере
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]