powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Отслеживание изменений записей - дёшево и сердито
9 сообщений из 9, страница 1 из 1
Отслеживание изменений записей - дёшево и сердито
    #33055033
Фотография XM
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Задача: отслеживание изменений (версий) данных - кто, когда, откуда изменил данные, сохранение версий данных с возможностью отката, сбор статистики по изменениям.
Контекст: web-приложение, PostgreSQL.
Быстрое и сердитое решение на скорую руку:
Код: 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.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
сreate table global_history (
        change_id bigint ,
        change_date timestamptz,
        change_table varchar( 64 ),
        change_user  varchar( 64 ), -- условно, в принципе -  ссылка на запись user_table (user_id)
        change_type  varchar( 20 ),
        change_host  varchar( 64 ) 
);

create sequence change_id_seq start  1 ;

create or replace function log_change(_table varchar( 64 ), _change_type varchar( 64 ),
                                      _where_clause text, _user varchar( 64 ),
                                      _host varchar( 64 )) returns void
AS $$
BEGIN
        EXECUTE 'insert into '||_table||'_history select nextval(''change_id_seq''), now(), ' ||
                quote_literal(_table)       || ',' ||
                quote_literal(_user)        || ',' ||
                quote_literal(_change_type) || ',' ||
                quote_literal(_host)        || ',' ||
                ' * from ' || _table || ' where ' || _where_clause;
        return;
END $$ language 'plpgsql';

-- тестовый пример использования
create table my_table (
        id int  primary key,
        data text
);

--  версии записей для "my_table"
create table my_table_history (
        like my_table,
        primary key (change_id)
) inherits (global_history);

-- отслеживание изменений
insert into my_table (id, data) values( 1 , 'test');
select log_change('my_table', 'create', 'id=1', 'anonymous', 'unknown');

update my_table set data='test2' where id =  1 ;
select log_change('my_table', 'update', 'id=1', 'anonymous', 'unknown');
Тестовые результаты:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
select * from my_table;
 id | data
----+-------
   1  | test2

select * from global_history;
 change_id |          change_date          | change_table | change_user | change_type | change_host
-----------+-------------------------------+--------------+-------------+-------------+-------------
          1  |  2005 - 05 - 09   12 : 48 : 44 . 157693 + 03  | my_table     | anonymous   | create      | unknown
          2  |  2005 - 05 - 09   12 : 48 : 44 . 157693 + 03  | my_table     | anonymous   | update      | unknown

select * from my_table_history;
 change_id |          change_date          | change_table | change_user | change_type | change_host | id | data
-----------+-------------------------------+--------------+-------------+-------------+-------------+----+-------
          1  |  2005 - 05 - 09   12 : 48 : 44 . 157693 + 03  | my_table     | anonymous   | create      | unknown     |   1  | test
          2  |  2005 - 05 - 09   12 : 48 : 44 . 157693 + 03  | my_table     | anonymous   | update      | unknown     |   1  | test2

Грызут меня сомнения, что такой велосипед уже давно где-то предложен, осмеян, и разобран на деревянные запчасти
Должно быть лучшее решение, не подскажете?
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33055039
Фотография Cat2
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
На мой взгляд, примерно "4" за курсовую.
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33055066
Фотография BusyMan
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не фиксируется вот что:

1. Какое поле изменили
2. Старое и новое значение поля
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33055112
Фотография XM
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BusyManНе фиксируется вот что:

1. Какое поле изменили
2. Старое и новое значение поля
А зачем? Пример ниже, как можно такого рода инфу получить и провести rollback
Код: 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.
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.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
create table global_history (
	change_id bigint ,
	change_date timestamptz,
	change_table varchar( 64 ),
	change_user  varchar( 64 ),
	change_type  varchar( 20 ),
	change_host  varchar( 64 )
);

create sequence change_id_seq start  1 ;

create or replace function log_change(_table varchar( 64 ), _change_type varchar( 64 ), 
				      _where_clause text, _user varchar( 64 ), 
				      _host varchar( 64 )) returns void 
AS $$
BEGIN
	EXECUTE 'insert into '||_table||'_history select nextval(''change_id_seq''), now(), ' || 

		quote_literal(_table)       || ',' ||
		quote_literal(_user)        || ',' ||
		quote_literal(_change_type) || ',' ||
		quote_literal(_host)        || ',' || 
		' * from ' || _table || ' where ' || _where_clause;
	return;
END $$ language 'plpgsql';

create table my_table (
	id int  primary key,
	data1 text,
	data2 text
);
CREATE TABLE
create table my_table_history (
	like my_table, 
	primary key (change_id)
) inherits (global_history);

create type DIFF_RECORD AS (
        att_name varchar( 20 ),
        att_changed boolean
);

create or replace function log_diff(_table varchar( 64 ), _change_id_1 bigint,
    _change_id_2 bigint) returns setof diff_record as $$
DECLARE log_row RECORD;
        chg_row RECORD;
        diff_row DIFF_RECORD;
BEGIN
FOR  log_row IN SELECT a.attname as att_name FROM pg_class c, pg_attribute a     
   WHERE c.relname = _table  AND a.attrelid = c.oid AND a.attnum> 0  ORDER BY a.attnum LOOP
        FOR chg_row IN EXECUTE
                'select (select '||log_row.att_name||' from '||_table||'_history where change_id='||text(_change_id_1)||
                ') <>   (select '||log_row.att_name||' from '||_table||'_history where change_id='||text(_change_id_2)||
                ') as changed' LOOP
                diff_row.att_name := log_row.att_name;
                diff_row.att_changed := chg_row.changed;
		if chg_row.changed IS NULL THEN diff_row.att_changed := true; END IF;
                RETURN NEXT diff_row;
        END LOOP;
END LOOP;
RETURN;
END $$ LANGUAGE 'plpgsql';

create or replace function log_rollback(_table varchar( 64 ), _change_id_1 bigint, _change_id_2 bigint, 
_where_clause text) returns void as $$
DECLARE _update_sql TEXT;
	diff_row DIFF_RECORD;
	_count INTEGER;
	_history_table TEXT;
BEGIN
	_count :=  0 ; 
	_history_table := _table || '_history';
	_update_sql := 'UPDATE '||_table||' SET ';
	FOR diff_row IN SELECT * FROM log_diff(_table, _change_id_1, _change_id_2) LOOP
	   IF diff_row.att_changed THEN
		IF _count >  0  THEN _update_sql := _update_sql || ' , '; END IF;
		_update_sql := _update_sql ||  
		  diff_row.att_name || ' = ' || _history_table || '.' || diff_row.att_name;
		_count := _count +  1 ;
	   END IF;
	END LOOP;
	IF _count >  0  THEN
		_update_sql := _update_sql || ' FROM '|| _history_table || ' WHERE '||_history_table
		|| '.change_id = ' || text(_change_id_1) || ' AND '||_where_clause;
		RAISE NOTICE 'PERFORMING QUERY: %', _update_sql;
		EXECUTE _update_sql;
	END IF;
	return;
END $$ language 'plpgsql';

insert into my_table (id, data1) values( 1 , 'test');
select log_change('my_table', 'create', 'id=1', 'anonymous', 'unknown');

update my_table set data1='test2' where id =  1 ;
select log_change('my_table', 'update', 'id=1', 'anonymous', 'unknown');

update my_table set data2='test3' where id =  1 ;
select log_change('my_table', 'update', 'id=1', 'anonymous', 'unknown');

select * from my_table;
 id | data1 | data2 
----+-------+-------
   1  | test2 | test3


select * from log_diff ('my_table',  1 ,  2 );
 att_name | att_changed 
----------+-------------
 id       | f
 data1    | t
 data2    | t


select * from log_diff ('my_table',  1 ,  3 );
 att_name | att_changed 
----------+-------------
 id       | f
 data1    | t
 data2    | t


select * from log_diff ('my_table',  2 ,  3 );
 att_name | att_changed 
----------+-------------
 id       | f
 data1    | f
 data2    | t


select log_rollback('my_table',  1 ,  3 , 'my_table.id=1');
NOTICE:  PERFORMING QUERY: UPDATE my_table SET data1 = my_table_history.data1 , 
data2 = my_table_history.data2 FROM my_table_history WHERE my_table_history.change_id =  1  AND my_table.id= 1 
  log_rollback 
--------------
 
select * from my_table;
 id | data1 | data2 
----+-------+-------
   1  | test  | 


select log_change('my_table','rollback to 1', 'id=1', 'anonymous', 'unknown');
 log_change 
------------
 

select * from global_history;
 change_id |         change_date          | change_table | change_user |  change_type  | change_host 
-----------+------------------------------+--------------+-------------+---------------+-------------
          1  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | create        | unknown
          2  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | update        | unknown
          3  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | update        | unknown
          4  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | rollback to  1  | unknown


select * from my_table_history;
 change_id |         change_date          | change_table | change_user |  change_type  | change_host | id | data1 | data2 
-----------+------------------------------+--------------+-------------+---------------+-------------+----+-------+-------
          1  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | create        | unknown     |   1  | test  | 
          2  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | update        | unknown     |   1  | test2 | 
          3  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | update        | unknown     |   1  | test2 | test3
          4  |  2005 - 05 - 09   14 : 58 : 09 . 06797 + 03  | my_table     | anonymous   | rollback to  1  | unknown     |   1  | test  | 

...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33055689
Фотография tygra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ниче не понял :) По-русски хоть бы описал, чего там делается.

У меня лично по простому и легкому работает: создается триггер, таблица-лог, клон той, что отслеживаем, при любой операции пишем в триггере в таблицу-лог и в общую таблицу, где написано кто, что, когда.
Даже софтина такая написана, чтобы включать-отключать аудит и просматривать, чего было.

Может выложить куда напоказ...... Правда для MS SQL.

-- Tygra's --
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33055760
Фотография XM
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tygra У меня лично по простому и легкому работает: создается триггер, таблица-лог, клон той, что отслеживаем, при любой операции пишем в триггере в таблицу-лог и в общую таблицу, где написано кто, что, когда.
Здесь тоже таблица-лог, клон отслеживаемой, только унаследована от общей таблицы global_history где написано кто, что, когда.
Внесение записей в лог организуется вызовом хп log_change единообразно для всех отслеживаемых таблиц - лень писать для каждой отдельно :).
Если отслеживать изменения, внесенные пользователями БД (которые create user ), то можно повесить на таблицы триггеры, которые будут автоматически сохранять изменения, вызывая log_change .
Проблема в том, что веб - приложения, как правило, имеют коннект от имени одного пользователя БД, а пользователи веб-приложения авторизуются не СУБД, а приложением. И отслеживать нужно изменения, созданные именно пользователями веб-приложения. :(
Мож, есть хорошее решение для такого случая?
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33055821
Фотография tygra
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да нет, тут только "насильно" отдавать в процедуры имя веб-пользователя, как его еще определишь?

-- Tygra's --
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33056213
Фотография PVP
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
tygraНиче не понял :) По-русски хоть бы описал, чего там делается.

У меня лично по простому и легкому работает: создается триггер, таблица-лог, клон той, что отслеживаем, при любой операции пишем в триггере в таблицу-лог и в общую таблицу, где написано кто, что, когда.
Даже софтина такая написана, чтобы включать-отключать аудит и просматривать, чего было.

Может выложить куда напоказ...... Правда для MS SQL.

-- Tygra's --Почему бы и нет? Я бы посморел.
Жаль, никто не занимается разработкой Web-beer.
...
Рейтинг: 0 / 0
Отслеживание изменений записей - дёшево и сердито
    #33056236
Tygra (Home)
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Выложу как нибудь, если найду куда. Тока она конечно без хелпов, и кое что руками сделать надо. Но работать работает.
...
Рейтинг: 0 / 0
9 сообщений из 9, страница 1 из 1
Форумы / Проектирование БД [игнор отключен] [закрыт для гостей] / Отслеживание изменений записей - дёшево и сердито
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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