postgresql - 使用 pl/pgsql INSERT 选择行

标签 postgresql

问题:有没有办法(也许是 pl/pgsql 函数方式?)使用我的日志表中找到的值(即值“action”、“schema_name”、“table_name”、“column_name”、“data_type”(即列数据类型)和“new_val”?)。

正在记录的表和我需要运行 INSERT/UPDATE/或 DELETE 的表如下所示:

enter image description here

..日志表如下所示:

enter image description here

...4 个突出显示的日志条目应插入到表中,如下所示:

enter image description here

...我正在尝试找到一种在另一个数据库表上运行插入/更新/或删除的方法(该表在名称/架构/等方面与正在记录的表相同)在日志表中选择特定的“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/

相关文章:

c++ - 使用 C++ 与 libpq 链接错误

sql - Postgres 选择数组具有相等值但没有特定顺序的位置

sql - 根据顺序随机选择行?

sql - PostgreSQL - 从列表中查找表中不存在的 ID

java - hibernate无法获取下一个序列值

postgresql - 如何在没有用户登录的情况下查看可用的postgres用户?

sql - Postgres 在表之间复制数据

sql - 如何根据 apache ignite 缓存中的日期获取我的值的总和

database - 为什么将数据两次添加到同一表?

javascript - 如何以编程方式自动化 sequelize 请求语句?