postgresql:嵌套插入

postgresql:嵌套插入,postgresql,stored-procedures,prepared-statement,Postgresql,Stored Procedures,Prepared Statement,我有两张桌子。比如tblA和tblB。 我需要在tblA中插入一行,并使用返回的id作为值插入tblB中的一列 我试图在文档中找到这一点,但没有得到它。那么,有没有可能写一个声明(打算在准备中使用)如 就像我们为SELECT做的那样 或者我应该通过创建存储过程来实现这一点?。我不确定是否可以用存储过程创建一个准备好的语句 请告知 问候, Mayank为此,您需要等待PostgreSQL 9.1: with ids as ( insert ... returning id ) insert ...

我有两张桌子。比如tblA和tblB。
我需要在tblA中插入一行,并使用返回的id作为值插入tblB中的一列

我试图在文档中找到这一点,但没有得到它。那么,有没有可能写一个声明(打算在准备中使用)如

就像我们为SELECT做的那样

或者我应该通过创建存储过程来实现这一点?。我不确定是否可以用存储过程创建一个准备好的语句

请告知

问候,

Mayank

为此,您需要等待PostgreSQL 9.1:

with
ids as (
insert ...
returning id
)
insert ...
from ids;

同时,您需要在应用程序中使用plpgsql、临时表或一些额外的逻辑…

这在9.0和新的
DO
中可以用于匿名块:

do $$
declare 
  new_id integer;
begin
  insert into foo1 (id) values (default) returning id into new_id;
  insert into foo2 (id) values (new_id);
end$$;
这可以作为单个语句执行。不过,我还没有试着用它来创建一个准备好的声明

编辑

另一种方法是分两步完成,首先使用returning子句将insert运行到tableA中,通过JDBC获取生成的值,然后启动第二个insert,如下所示:

PreparedStatement stmt_1 = con.prepareStatement("INSERT INTO tblA VALUES (DEFAULT, ?) returning id");
stmt_1.setString(1, "x");
stmt_1.execute(); // important! Do not use executeUpdate()!
ResultSet rs = stmt_1.getResult();
long newId = -1;
if (rs.next()) {
   newId = rs.getLong(1);
}
PreparedStatement stmt_2 = con.prepareStatement("INSERT INTO tblB VALUES (default,?,?)");
stmt_2.setLong(1, newId);
stmt_2.setString(2, "y");
stmt_2.executeUpdate();

您可能希望在插入后使用
触发器。大致如下:

create function dostuff() returns trigger as $$
begin
 insert into table_b(field_1, field_2) values ('foo', NEW.id);
 return new; --values returned by after triggers are ignored, anyway
end;
$$ language 'plpgsql';

create trigger trdostuff after insert on table_name for each row execute procedure dostuff();
插入后需要
,因为您需要有引用它的id。希望这有帮助

编辑


触发器将在与触发它的命令相同的“块”中调用,即使不使用事务——换句话说,它在某种程度上成为该命令的一部分。。因此,不存在在插入之间更改引用id的风险。

您可以在两次插入中执行此操作,使用
currval()
检索外键(前提是键是串行的):

结果是:

select * from tb1a;
 id | t
----+---
  3 | x
(1 row)

select * from tb1b;
 id | tb1a_id | t
----+---------+---
  2 |       3 | y
(1 row)
无论是在事务内部还是外部,以这种方式使用currval都是安全的。从:

柯尔瓦尔

返回最近的值 由nextval为此序列获取 在本届会议上。(错误是 如果nextval从未被 在本例中调用此序列 会话。),因为这将返回 会话本地值,它给出一个 可预测的答案是否 其他会话已执行nextval 自本届会议以来


也许我是个偏执狂,但依靠
currval
不是最好的主意。首先,它假定id基于序列。其次,除非显式使用事务,否则键的值可能会在插入之间发生变化,这会破坏完整性。第三,它是不可重用的。但是,这只是我的观点,你不必同意:)@sary,我编辑了答案,以解决你对钥匙价值的担忧。对于其他指控,我请求诺洛申辩。@Waynce,你的编辑澄清了这个问题。因此,我的评论现在应该指出“依赖
currval
是一个很好的选择,只要id绑定到序列”。尽管第三个问题仍然存在,但这是一个设计问题,不包括在问题中,可能会引发一场无休止的辩论,所以最好到此为止。@悲伤,我同意,它完全不可移植。无论如何,谢谢你帮助我把答案做得更好。
create temporary table tb1a (id serial primary key, t text);
create temporary table tb1b (id serial primary key,
                             tb1a_id int references tb1a(id),
                             t text);
begin;
insert into tb1a values (DEFAULT, 'x');
insert into tb1b values (DEFAULT, currval('tb1a_id_seq'), 'y');
commit;
select * from tb1a;
 id | t
----+---
  3 | x
(1 row)

select * from tb1b;
 id | tb1a_id | t
----+---------+---
  2 |       3 | y
(1 row)