Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Firebird, InterBase [игнор отключен] [закрыт для гостей] / Поиск измененных полей при обновлении строки в триггере / 25 сообщений из 28, страница 1 из 2
25.02.2014, 07:27:52
    #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
25.02.2014, 08:05:44
    #38571137
Гхостик
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
lamborgine,

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

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

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

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

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

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

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

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

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

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

Пробовал, не получилось. Если есть идеи как, напиши.
Печка , от которой можно начинать плясать.
...
Рейтинг: 0 / 0
25.02.2014, 11:53:55
    #38571384
lamborgine
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
ExterisЧто ты потом будешь с этим списком полей делать?
Мне это нужно для логов работы с таблицами.
...
Рейтинг: 0 / 0
25.02.2014, 12:00:56
    #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
25.02.2014, 12:03:16
    #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
25.02.2014, 12:04:13
    #38571401
Exteris
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
И да, лучше is distinct.
...
Рейтинг: 0 / 0
25.02.2014, 12:05:19
    #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
25.02.2014, 12:07:43
    #38571405
lamborgine
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
ExterisНадеюсь массовых изменений не планируется?
Массовых нет, но периодические да.
...
Рейтинг: 0 / 0
25.02.2014, 13:01:06
    #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
25.02.2014, 13:29:17
    #38571538
Exteris
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
Гхостик2 Exteris
Попробовал, не работает. Да и понятно - откуда sql-ю знать переменную из psql?

Точно же.(
...
Рейтинг: 0 / 0
25.02.2014, 13:51:10
    #38571564
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
lamborgineМассовых нет, но периодические да.
Переходи на тройку, там есть DDL триггера, сможешь генерировать новый текст триггера при
изменении структуры таблицы автоматически. Или просто проверяй в триггере число полей
таблицы и если больше ожидаемого - выкидывай исключение, чтобы тот, кто таблицу расширил,
а про протоколирование забыл, исправил свою ошибку.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
25.02.2014, 14:00:38
    #38571579
lamborgine
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
Dimitry Sibiryakov
По ходу так и придется делать..
...
Рейтинг: 0 / 0
25.02.2014, 14:05:49
    #38571584
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
Ну или можешь контролировать скрипт изменения БД каждого балбеса перед применением. Забыть
добавить поле в триггер - не самая худшая ошибка, которую они могут допустить...
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
25.02.2014, 15:22:55
    #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
25.02.2014, 15:37:28
    #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
25.02.2014, 15:42:45
    #38571771
dennis-r
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Поиск измененных полей при обновлении строки в триггере
Таблоид,

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


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