将行插入到一个查询中的多个表中,从一个涉及的表中进行select

我有两个表格(即,每一个foo链接到一个吧)。

CREATE TABLE foo ( id INTEGER PRIMARY KEY, x INTEGER NOT NULL, y INTEGER NOT NULL, ..., bar_id INTEGER UNIQUE NOT NULL, FOREIGN key (bar_id) REFERENCES bar(id) ); CREATE TABLE bar ( id INTEGER PRIMARY KEY, z INTEGER NOT NULL, ... ); 

foo中使用嵌套查询来满足特定条件很容易:

 INSERT INTO foo (...) (SELECT ... FROM foo WHERE ...) 

但我无法弄清楚如何在foo为每行创build一个关联的行的副本,并将bar的id插入到新的foo行中。 有没有办法在一个查询中做到这一点?

期望结果的具体例子:

 -- Before query: foo(id=1,x=3,y=4,bar_id=100) ..... bar(id=100,z=7) foo(id=2,x=9,y=6,bar_id=101) ..... bar(id=101,z=16) foo(id=3,x=18,y=0,bar_id=102) ..... bar(id=102,z=21) -- Query copies all pairs of foo/bar rows for which x>3: -- Originals foo(id=1,x=3,y=4,bar_id=101) ..... bar(id=101,z=7) foo(id=2,x=9,y=6,bar_id=102) ..... bar(id=102,z=16) foo(id=3,x=18,y=0,bar_id=103) ..... bar(id=103,z=21) -- "Copies" of foo(id=2,...) and foo(id=3,...), with matching copies of -- bar(id=102,...) and bar(id=103,...) foo(id=4,x=9,y=6,bar_id=104) ..... bar(id=104,z=16) foo(id=5,x=18,y=0,bar_id=105) ..... bar(id=105,z=21) 

最终版本

…从OP之后更多的信息。 考虑这个演示:

 -- DROP TABLE foo; DROP TABLE bar; CREATE TEMP TABLE bar ( id serial PRIMARY KEY -- using a serial column! ,z integer NOT NULL ); CREATE TEMP TABLE foo ( id serial PRIMARY KEY -- using a serial column! ,x integer NOT NULL ,y integer NOT NULL ,bar_id integer UNIQUE NOT NULL REFERENCES bar(id) ); 

插入值 – bar第一。
如果你在这个问题中提供了testing数据,这将是非常有帮助的

 INSERT INTO bar (id,z) VALUES (100, 7) ,(101,16) ,(102,21); INSERT INTO foo (id, x, y, bar_id) VALUES (1, 3,4,100) ,(2, 9,6,101) ,(3,18,0,102); 

将序列设置为当前值或者我们得到重复的键违规:

 SELECT setval('foo_id_seq', 3); SELECT setval('bar_id_seq', 102); 

检查:

 -- SELECT nextval('foo_id_seq') -- SELECT nextval('bar_id_seq') -- SELECT * from bar; -- SELECT * from foo; 

查询:

 WITH a AS ( SELECT fx, fy, bar_id, bz FROM foo f JOIN bar b ON b.id = f.bar_id WHERE x > 3 ),b AS ( INSERT INTO bar (z) SELECT z FROM a RETURNING z, id AS bar_id ) INSERT INTO foo (x, y, bar_id) SELECT ax, ay, b.bar_id FROM a JOIN b USING (z); 

这应该做你最近的更新描述。

该查询假定zUNIQUE 。 如果z不唯一,则会变得更复杂。 在这种情况下,使用窗口函数row_number() ,参考相关答案中的查询2获得一个现成的解决scheme。

另外,考虑用一个统一的表格replacefoobar之间的1:1关系


数据修改CTE

第二个答案后更多的信息。

如果要在单个查询中将行添加到foo bar ,可以使用PostgreSQL 9.1以来的数据修改CTE :

 WITH x AS ( INSERT INTO bar (col1, col2) SELECT f.col1, f.col2 FROM foo f WHERE f.id BETWEEN 12 AND 23 -- some filter RETURNING col1, col2, bar_id -- assuming bar_id is a serial column ) INSERT INTO foo (col1, col2, bar_id) SELECT col1, col2, bar_id FROM x; 

我从foo绘制值,将它们插入到bar ,让它们与自动生成的bar_id一起返回并将其插入到foo 。 您也可以使用任何其他数据。

这是一个在sqlfiddle上玩的工作演示 。


基本

澄清前的基本信息的原始答复。
基本forms是:

 INSERT INTO foo (...) SELECT ... FROM foo WHERE ... 

不需要括号。 您可以对任何表格执行相同的操作

 INSERT INTO foo (...) SELECT ... FROM bar WHERE ... 

你可以join你在SELECT中插入的表格:

 INSERT INTO foo (...) SELECT f.col1, f.col2, .. , b.bar_id FROM foo f JOIN bar b USING (foo_id); -- present in foo and bar 

这只是一个像任何其他的select – 可以包括你插入的表。 行首先被读取,然后被插入。

bar id是串行的,并且具有默认值nextval('bar_id_seq'::regclass)你可以手动调用它来获得新的id在cte

 with s_bar as ( SELECT id, z, nextval('bar_id_seq'::regclass) new_id FROM bar WHERE ... ) s_foo as ( SELECT x, y, bar_id FROM foo WHERE ... ) i_bar as ( INSERT INTO bar (id, z) SELECT new_id, z FROM s_bar ) i_foo as ( INSERT INTO foo (x, y, bar_id) SELECT fx, fy, b.new_id FROM s_foo f JOIN s_bar b on b.id = f.bar_id ) SELECT 1