问题:有没有办法(也许是 pl/pgsql 函数方式?)使用我的日志表中找到的值(即值“action”、“schema_name”、“table_name”、“column_name”、“data_type”(即列数据类型)和“new_val”?)。
正在记录的表和我需要运行 INSERT/UPDATE/或 DELETE 的表如下所示:
..日志表如下所示:
...4 个突出显示的日志条目应插入到表中,如下所示:
...我正在尝试找到一种在另一个数据库表上运行插入/更新/或删除的方法(该表在名称/架构/等方面与正在记录的表相同)在日志表中选择特定的“usr”和“event_date”后。
为了获得我想要的结果(仅适用于 INSERT 语句 - 见下文),SQL 非常复杂 ( demo in SQL FIDDLE )。我很想知道是否有其他方法可能......
INSERT INTO Engineering.Elective_Courses
(gid, grade, class, student_id)
WITH
t1 AS
(Select new_val
From student.history
WHERE
column_name = 'gid'
AND
usr = 'Principal K.'
AND
(event_date >= '2017-01-26' AND event_date < '2017-01-29')),
t2 AS (Select new_val
From student.history
WHERE
column_name = 'grade'
AND
usr = 'Principal K.'
AND
(event_date >= '2017-01-26' AND event_date < '2017-01-29')),
t3 AS (Select new_val
From student.history
WHERE
column_name = 'class'
AND
usr = 'Principal K.'
AND
(event_date >= '2017-01-26' AND event_date < '2017-01-29')),
t4 AS (Select new_val
From student.history
WHERE
column_name = 'student_id'
AND
usr = 'Principal K.'
AND
(event_date >= '2017-01-26' AND event_date < '2017-01-29'))
select t1.new_val::int, t2.new_val, t3.new_val, t4.new_val::int
from t1,t2, t3, t4;
最佳答案
您必须使用dynamic SQL .
此查询聚合各个操作的数据:
select
action, event_date, usr,
schema_name, table_name, pkey_id,
string_agg(quote_ident(column_name), ',' order by history_id) as cols,
-- string_agg(quote_literal(new_val), ',' order by history_id) as vals
-- correction:
string_agg(coalesce(quote_literal(new_val), 'null'), ',' order by history_id) as vals
from student.history
group by 1, 2, 3, 4, 5, 6;
action | event_date | usr | schema_name | table_name | pkey_id | cols | vals
--------+---------------------+--------------+-------------+------------------+---------+----------------------------+----------------------------
DELETE | 2017-01-28 12:20:03 | Ast. Dean J. | Engineering | Elective_Courses | 14 | grade |
INSERT | 2017-01-26 22:42:53 | Principal K. | Engineering | Elective_Courses | 12 | gid,grade,class,student_id | '12','B-','PYS7C','607752'
UPDATE | 2017-01-26 22:42:53 | Ast. Dean J. | Engineering | Elective_Courses | 13 | grade | 'C'
(3 rows)
为了更好的安全性,历史表应该有一个额外的唯一的 Action ID 来区分,例如:同一用户同时在同一个表上进行两次插入,尽管这种巧合不太可能发生。
该函数基于上述查询。它有一个参数,即历史表上 WHERE
子句的文本。它返回生成的查询。
它还可以执行查询(在注释的代码段中)。
create or replace function restore_log(condition text)
returns setof text language plpgsql as $$
declare
relid regclass;
pkey text;
query text;
rec record;
begin
for rec in
execute format('
select
action, event_date, usr,
schema_name, table_name, pkey_id,
string_agg(quote_ident(column_name), '','' order by history_id) as cols,
string_agg(coalesce(quote_literal(new_val), ''null''), '','' order by history_id) as vals
from student.history
where %s
group by 1, 2, 3, 4, 5, 6',
condition)
loop
relid:= format('%s.%s', rec.schema_name, rec.table_name)::regclass;
pkey:= get_pkey_name(relid); -- see below
query:= case rec.action
when 'INSERT' then
format(
'insert into %s.%s (%s) values (%s)',
rec.schema_name, rec.table_name, rec.cols, rec.vals)
when 'UPDATE' then
format(
'update %s.%s set (%s) = (%s) where %s = %s',
rec.schema_name, rec.table_name, rec.cols, rec.vals, pkey, rec.pkey_id)
when 'DELETE' then
format(
'delete from %s.%s where %s = %s',
rec.schema_name, rec.table_name, pkey, rec.pkey_id)
else null end;
return next query;
-- if query not null then
-- execute(query);
-- end if;
end loop;
end $$;
使用示例:
select * from restore_log('true');
restore_log
-----------------------------------------------------------------------------------------------------------
delete from Engineering.Elective_Courses where gid = 14
insert into Engineering.Elective_Courses (gid,grade,class,student_id) values ('12','B-','PYS7C','607752')
update Engineering.Elective_Courses set (grade) = ('C') where gid = 13
(3 rows)
select * from restore_log($$
usr = 'Principal K.'
and event_date >= '2017-01-26'
and event_date < '2017-01-29'$$);
restore_log
-----------------------------------------------------------------------------------------------------------
insert into Engineering.Elective_Courses (gid,grade,class,student_id) values ('12','B-','PYS7C','607752')
(1 row)
查找给定表的单列主键列名的函数(在restore_log()
中使用):
create or replace function get_pkey_name(regclass)
returns name language sql as $$
select attname
from pg_constraint c
join pg_attribute a on attrelid = conrelid and attnum = conkey[1]
where conrelid = $1
and contype = 'p'
and cardinality(conkey) = 1
$$;
安全注意,出于安全原因,基本上你应该使用 format('%I.%I, schema_name, table_name)
,但在这种情况下,由于使用大写字母,它会给出错误的结果数据中。
关于postgresql - 使用 pl/pgsql INSERT 选择行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41904958/