使用PERFORM数据修改CTE查询的Postgres plpgsql

时间:2017-07-04 14:23:18

标签: postgresql plpgsql common-table-expression

我试图在下面的代码示例中模拟我的问题。在下面的代码中,我在一个过程中执行delete from test2。这非常有效:

但是,在我的情况下,这个delete是一个相当复杂的CTE的一部分,有几个更新和插入(没有选择,所以我添加一个虚拟select 1作为主查询)。让我们模拟这个:

with my_cte as(delete from test2) select 1

现在,正如我们所知,我们必须使用perform关键字来执行此操作:

perform (with my_cte as(delete from test2) select 1);

我收到以下错误:

ERROR: WITH clause containing a data-modifying statement must be at the top level

这是plpgsql的限制吗?

(请注意,这只是解释我的问题的一个例子。我知道这些查询没有任何意义。)

create table test
(
    key int primary key  
);

create table test2
(
    key int primary key
);

create function test() returns trigger as
$$
begin
    raise notice 'hello there';
    -- this does work
    delete from test2;
    -- this doesn't work
    perform (with my_cte as(delete from test2) select 1);
    return new;
end;
$$
language plpgsql;

create trigger test after insert on test for each row execute procedure test();

insert into test(key) select 1;

2 个答案:

答案 0 :(得分:2)

您可以使用CTE组合多个DELETE,INSERT,UPDATE返回查询。而且你不需要为它执行,例如:

t=# begin; do $$ begin with d as (delete from s133 returning *) insert into s133 select * from d; raise info '%',(select count(1) from s133);
end; $$; commit;
BEGIN
Time: 0.135 ms
INFO:  4
DO
Time: 0.469 ms
COMMIT
Time: 0.887 ms
t=# select count(1) from s133;
 count
-------
     4
(1 row)

这里我删除了四行,然后在CTE中插入它们

答案 1 :(得分:1)

正如您所发现的那样,您既不能在子选择中嵌套这样的Timesheet::WorkPeriod.joins(:ship).limit(2000).pluck('ships.id') # You may also want left join Timesheet::WorkPeriod .joins('LEFT OUTER JOIN ships ON ships.work_period_id = work_periods.ids') .limit(2000).pluck('ships.id') 子句,也不会这样做

WITH

一种解决方案是使用WITH cte AS (...) PERFORM 1; 代替SELECT ... INTO dummy并忽略结果。

但我不明白为什么你不能用你的函数用PERFORMDELETEUPDATE编写几个SQL语句而不是将它们捆绑到CTE中。

如果您试图保护自己免受并发数据修改的影响,请使用INSERT事务,以便所有语句都在数据库的同一快照上运行。