рд▓рдбрд╝рдирд╛ рдбреБрдкреНрд▓рд┐рдХреЗрдЯ

рдЧрддрд┐рд╢реАрд▓ рдПрд╕рдХреНрдпреВрдПрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд╡рд┐рд╖рдп рдХреЛ рдЬрд╛рд░реА рд░рдЦрддреЗ рд╣реБрдП, рдореИрдВ рдПрдХ рдЙрдкрдпреЛрдЧреА рдЙрдкрдХрд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВ рдЬрд┐рд╕реЗ рдореИрдВрдиреЗ рд╡рд░реНрддрдорд╛рди рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдХреЗ рднрд╛рдЧ рдХреЗ рд░реВрдк рдореЗрдВ рд▓рд╛рдЧреВ рдХрд┐рдпрд╛ рд╣реИред рдпрд╣ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛рдУрдВ рдореЗрдВ рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реЛрдЧрд╛ред рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рджреНрд╡рд╛рд░рд╛, рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореЗрд░рд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛рдУрдВ рдореЗрдВ рдмрдирд╛рдИ рдЧрдИ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐рдпрд╛рдБ, рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдирд╛рдо рджрд░реНрдЬ рдХрд░рддреЗ рд╕рдордп рд╡рд░реНрддрдиреА рдХреА рддреНрд░реБрдЯрд┐ рдХреЗ рдкрд░рд┐рдгрд╛рдорд╕реНрд╡рд░реВрдкред

рдореЗрд░реЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХрд╛ рд╕рд╛рд░ рдХрд┐рд╕реА рднреА рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХреЛ рдореМрдЬреВрджрд╛ рдХреЗ рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рдШреЛрд╖рд┐рдд рдХрд░рдирд╛ рд╕рдВрднрд╡ рд╣реИред рдирддреАрдЬрддрди, рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХреЛ рд╣рдЯрд╛ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рд╕рднреА рд╕рдВрджрд░реНрднреЛрдВ рдХреЛ рд╕рд╣реА рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ рддрд╛рдХрд┐ рд╡реЗ рд╕рд╣реА рдкреНрд░рд╡рд┐рд╖реНрдЯрд┐ рдХрд╛ рд╕рдВрджрд░реНрдн рджреЗрдВред рдЗрд╕ рддрд░рд╣ рдХреЗ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ рдкреНрд░рджрд╛рди рдХрд░рдирд╛ рднреА рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ, рдпрджрд┐ рд╡реЗ рдЧрд▓рддреА рд╕реЗ рдмрдиреЗ рд╣реИрдВред

рдЪрд▓реЛ рд╕реЗрд╡рд╛ рдбреЗрдЯрд╛ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ:

рд╕реЗрд╡рд╛ рддрд╛рд▓рд┐рдХрд╛рдУрдВ
create table mg_table ( table_name varchar(100) not null, pk_name varchar(100) not null, primary key(table_name) ); create sequence mg_action_seq; create table mg_action ( id bigint default nextval('mg_action_seq') not null, table_name varchar(100) not null references mg_table(table_name), old_id varchar(50) not null, new_id varchar(50) not null, action_time timestamp default now() not null, primary key(id) ); create sequence mg_action_detail_seq; create table mg_action_detail ( id bigint default nextval('mg_action_detail_seq') not null, action_id bigint not null references mg_action(id), table_name varchar(100) not null, pk_name varchar(100) not null, column_name varchar(100) not null, obj_id varchar(50) not null, primary key(id) ); 


рдпрд╣рд╛рдВ, mg_table рдореЗрдВ рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдбреЗрдЯрд╛ рд╣реЛрддрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рд╡рд┐рд▓рдп рдХрд╛ рд╕рдорд░реНрдерди рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдРрд╕реА рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдПрдХрдорд╛рддреНрд░ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдпрд╣ рд╣реИ рдХрд┐ рдкреНрд░рд╛рдердорд┐рдХ рдХреБрдВрдЬреА рдореЗрдВ рдПрдХ рдПрдХрд▓ рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рдпрд╛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЙрд▓рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рд╣рдореЗрдВ рдЗрд╕ рддрд╛рд▓рд┐рдХрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЪрд┐рдВрддрд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рднрд░ рдЬрд╛рдПрдЧреАред Mg_action рдФрд░ mg_action_detail рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдореЗрдВ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рд╡рд╛рдкрд╕ рд░реЛрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рдбреЗрдЯрд╛ рд╣реЛрдЧрд╛ред

рд╣рдо рд╕рд╣рд╛рдпрдХ рдХрд╛рд░реНрдпреЛрдВ рдХреА рдПрдХ рдЬреЛрдбрд╝реА рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ:

рд╣реЗрд▓реНрдкрд░ рдХрд╛рд░реНрдп
 create or replace function mg_get_pk_column(in p_table varchar) returns varchar as $$ declare l_pk text; l_cn int; begin select max(f.name), count(*) as name into l_pk, l_cn from ( select ps_array_to_set(a.conkey) as nn from pg_constraint a, pg_class b where b.oid = a.conrelid and a.contype = 'p' and b.relname = lower(p_table) ) c, ( select d.attname as name, d.attnum as nn from pg_attribute d, pg_class e where e.oid = d.attrelid and e.relname = lower(p_table) ) f where f.nn = c.nn; if l_cn <> 1 then raise EXCEPTION 'Can''t support composite PK'; end if; return l_pk; end; $$ language plpgsql; create or replace function mg_add_dict(in p_table varchar) returns void as $$ declare l_pk text; l_sql text; begin l_pk := mg_get_pk_column(p_table); perform 1 from mg_table where table_name = lower(p_table); if not FOUND then l_sql := 'create table mg_' || lower(p_table) || ' ' || 'as select * from ' || lower(p_table) || ' limit 0'; execute l_sql; l_sql := 'alter table mg_' || lower(p_table) || ' ' || 'add primary key(' || l_pk || ')'; execute l_sql; insert into mg_table(table_name, pk_name) values (lower(p_table), l_pk); end if; end; $$ language plpgsql; 


Mg_get_pk_column рдлрд╝рдВрдХреНрд╢рди рдкрд┐рдЫрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдЬреНрдЮрд╛рдд рдХреНрд╡реЗрд░реА рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рдкреНрд░рд╛рдердорд┐рдХ рдХреБрдВрдЬреА рдХреЙрд▓рдо рдХрд╛ рдирд╛рдо рджреЗрддрд╛ рд╣реИ, рдФрд░ рдпрд╣ рднреА рдЬрд╛рдВрдЪрддрд╛ рд╣реИ рдХрд┐ рдкреНрд░рд╛рдердорд┐рдХ рдХреБрдВрдЬреА рдореЗрдВ рдПрдХ рдХреЙрд▓рдо рд╣реЛрддрд╛ рд╣реИред

Mg_add_dict рдлрд╝рдВрдХреНрд╢рди, mg_table рднрд░рдиреЗ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЙрдкрд╕рд░реНрдЧ 'mg_' рдХреЗ рд╕рд╛рде рдПрдХ рддрд╛рд▓рд┐рдХрд╛ рдмрдирд╛рддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рд╣рдЯрд╛рдП рдЧрдП рдбреБрдкреНрд▓рд┐рдХреЗрдЯ рд╕рд╣реЗрдЬреЗ рдЬрд╛рдПрдВрдЧреЗ, рдпрджрд┐ рдкрд░рд┐рд╡рд░реНрддрди рдХреЛ рд╡рд╛рдкрд╕ рд░реЛрд▓ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕рдХреА рд╕рдВрд░рдЪрдирд╛ рдореЗрдВ, рдпрд╣ рддрд╛рд▓рд┐рдХрд╛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдореВрд▓ рдХреЗ рд╕рдорд╛рди рд╣реИред

рд╣рдо рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИ:

mg_merge
 create or replace function mg_merge(in p_table varchar, in p_old varchar, in p_new varchar) returns void as $$ declare l_action int; l_pk text; l_sql text; tabs record; begin perform mg_add_dict(p_table); select pk_name into l_pk from mg_table where table_name = lower(p_table); l_action := nextval('mg_action_seq'); insert into mg_action(id, table_name, old_id, new_id) values (l_action, p_table, p_old, p_new); l_sql := 'insert into mg_' || lower(p_table) || ' ' || 'select * from ' || lower(p_table) || ' ' || 'where ' || l_pk || ' = ''' || p_old || ''''; execute l_sql; for tabs in select b.relname as table_name, d.attname as column_name from pg_constraint a, pg_class b, pg_class c, pg_attribute d where a.contype = 'f' and b.oid = a.conrelid and c.oid = a.confrelid and c.relname = lower(p_table) and d.attrelid = b.oid and a.conkey[1] = d.attnum loop l_sql := 'insert into mg_action_detail(action_id, table_name, column_name, obj_id, pk_name) ' || 'select ' || l_action || ', ''' || tabs.table_name || ''', ''' || tabs.column_name || ''', id, ' || '''' || mg_get_pk_column(tabs.table_name::varchar) || ''' ' || 'from ' || lower(tabs.table_name) || ' ' || 'where ' || lower(tabs.column_name) || ' = ''' || p_old || ''''; execute l_sql; l_sql := 'update ' || lower(tabs.table_name) || ' ' || 'set ' || lower(tabs.column_name) || ' = ''' || p_new || ''' ' || 'where ' || lower(tabs.column_name) || ' = ''' || p_old || ''''; execute l_sql; end loop; l_sql := 'delete from ' || lower(p_table) || ' where ' || l_pk || ' = ''' || p_old || ''''; execute l_sql; end; $$ language plpgsql; create or replace function mg_merge(in p_table varchar, in p_old bigint, in p_new bigint) returns void as $$ declare begin perform mg_merge(p_table, p_old::varchar, p_new::varchar); end; $$ language plpgsql; 


рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рд╕рднреА рддрд╛рд▓рд┐рдХрд╛рдУрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЦреЛрдЬ рдХрд░рддрд╛ рд╣реИ рдЬреЛ рдПрдХ рд╡рд┐рджреЗрд╢реА рдХреБрдВрдЬреА рдХреЗ рд╕рд╛рде p_table рдХреЛ рд╕рдВрджрд░реНрднрд┐рдд рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЙрдирдореЗрдВ p_new рдХреЗ рд╕рд╛рде p_old рдХреЛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд░рддрд╛ рд╣реИ, рдбреЗрдЯрд╛ рдХреЛ рд╡рд╛рдкрд╕ рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рд░реЛрд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╕рд╣реЗрдЬрддрд╛ рд╣реИред рдЪреВрдВрдХрд┐, рд╕рдмрд╕реЗ рдЕрдзрд┐рдХ рдмрд╛рд░, рдкреНрд░рд╛рдердорд┐рдХ рдХреБрдВрдЬреА рдХреЙрд▓рдо рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рд╣реЛрдЧрд╛, рд╕реБрд╡рд┐рдзрд╛ рдХреЗ рд▓рд┐рдП, mg_merge рдлрд╝рдВрдХреНрд╢рди (varchar, bigint, bigint) рдЕрддрд┐рднрд╛рд░рд┐рдд рд╣реИред

рдпрд╣ рд░реЛрд▓рдмреИрдХ рдлрд╝рдВрдХреНрд╢рди рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдмрдирд╛ рд╣реБрдЖ рд╣реИ:

mg_undo
 create or replace function mg_undo() returns void as $$ declare l_action int; l_old varchar(50); l_table text; l_sql text; tabs record; begin select max(id) into l_action from mg_action; if l_action is null then raise EXCEPTION 'Can''t UNDO'; end if; select table_name, old_id into l_table, l_old from mg_action where id = l_action; l_sql := 'insert into ' || l_table || ' ' || 'select * from mg_' || l_table || ' ' || 'where id = ''' || l_old || ''''; execute l_sql; for tabs in select table_name, pk_name, column_name from mg_action_detail where action_id = l_action group by table_name, pk_name, column_name loop l_sql := 'update ' || tabs.table_name || ' ' || 'set ' || tabs.column_name || ' = ''' || l_old || ''' ' || 'where '''' || ' || tabs.pk_name || ' in (' || 'select '''' || obj_id from mg_action_detail '|| 'where table_name = ''' || tabs.table_name || ''' ' || 'and action_id = ' || l_action || ') '; execute l_sql; end loop; l_sql := 'delete from mg_' || l_table || ' where id = ''' || l_old || ''''; execute l_sql; delete from mg_action_detail where action_id = l_action; delete from mg_action where id = l_action; end; $$ language plpgsql; 


рдХрдбрд╝рд╛рдИ рд╕реЗ рдЙрдирдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд╡рд┐рдкрд░реАрдд рдХреНрд░рдо рдореЗрдВ рд╡рд╛рдкрд╕ рд░реЛрд▓ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рдЗрд╕ рдХрд╛рд░рдг рд╕реЗ, mg_undo рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдИ рддрд░реНрдХ рдЖрд╡рд╢реНрдпрдХ рдирд╣реАрдВ рд╣реИред

рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдпрд╣ рд╕рдм рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рд▓реБрдХрдЕрдк рдЯреЗрдмрд▓ рдмрдирд╛рдПрдВ:

 create sequence city_seq; create table city ( id bigint default nextval('city_seq') not null, name varchar(100) not null, primary key(id) ); create sequence street_seq; create table street ( id bigint default nextval('street_seq') not null, city_id bigint not null references city(id), name varchar(100) not null, primary key(id) ); create sequence address_seq; create table address ( id bigint default nextval('address_seq') not null, street_id bigint not null references street(id), house varchar(10) not null, apartment varchar(10) not null, primary key(id) ); 

... рдФрд░ рдЙрдиреНрд╣реЗрдВ рдкрд░реАрдХреНрд╖рдг рдбреЗрдЯрд╛ рдХреЗ рд╕рд╛рде рднрд░реЗрдВ:

 insert into city(id, name) values (1, ''); insert into street(id, city_id, name) values (1, 1, ''); insert into street(id, city_id, name) values (2, 1, ' '); insert into address(id, street_id, house, apartment) values (1, 1, '10', '1'); insert into address(id, street_id, house, apartment) values (2, 2, '10', '2'); 


рдЕрдм рд╡рд┐рдЬрдп рд╕реНрдЯреНрд░реАрдЯ рдХреЗ рд╕рд╛рде рд╡рд┐рдЬрдп рдПрд╡реЗрдиреНрдпреВ рд╕рдбрд╝рдХ рдХреЛ "рдорд░реНрдЬ" рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдпрд╣ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХрдорд╛рдВрдб рдХреЛ рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рд╣реИ:

 select mg_merge('street', 2, 1); 

Mg_undo () рдлрд╝рдВрдХреНрд╢рди, рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рдмрддрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдкрд░рд┐рд╡рд░реНрддрдиреЛрдВ рдХреЛ рд╡рд╛рдкрд╕ рд▓рд╛рдПрдЧрд╛ред

рдЖрд╢рд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХрд┐рд╕реА рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧреА рдерд╛ред рдЧреАрдерд╣рдм рдкрд░ рд╕реВрддреНрд░ рдкреЛрд╕реНрдЯ рдХрд┐рдП рдЧрдП рд╣реИрдВред

Source: https://habr.com/ru/post/In179789/


All Articles