问题:是否有一种方法(可能是pl / pgsql函数方式?)使用我的日志表中找到的值在特定表上创建INSERT / UPDATE / DELETE查询(即值'操作',' schema_name',' table_name',' column_name',' data_type'(即列数据type)和' new_val'?)。
正在记录的表和我需要运行INSERT / UPDATE /或DELETE的表如下所示:
..并且日志表如下所示:
... 4个突出显示的日志条目应该像这样插入到表中:
...我试图找到一种方法在 ANOTHER DATABASE表上运行INSERT / UPDATE /或DELETE(这与表中的名称/模式/等相同)选择具体的' 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;
答案 0 :(得分:2)
您必须使用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)
为了更好的安全性,历史表应该有一个额外的唯一动作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)
,但在这种情况下,由于在数据中使用大写字母,它会产生错误的结果。
答案 1 :(得分:1)
如果要插入多行,则显示的查询中的交叉连接将生成错误的笛卡尔积。永远不要使用它。
如果目标表是预定义的,那么普通crosstab()
查询就可以完成工作:
INSERT INTO engineering.elective_courses (gid, grade, class, student_id)
SELECT * FROM crosstab(
$$SELECT pkey_id, column_name, new_val
FROM student.history
WHERE usr = 'Principal K.' -- your criteria here
AND event_date >= '2017-01-26'
AND event_date < '2017-01-29'
AND action = 'INSERT'
ORDER BY 1$$
, $$SELECT unnest('{grade,class,student_id}'::text[])$$)
AS ct (gid int, grade varchar, class varchar, student_id int);
gid
(示例中为history_id = 0
)的额外行必须与列pkey_id
匹配,并且完全是多余的。 crosstab()
只是忽略了它,因为&#39; gid&#39;未在第二个函数参数中列为目标列。
详细说明:
如果不应预定义目标表,也可以动态创建语句。密切相关的答案:
高级: