我有两个以下形式的表(即,每个 foo 都链接到一个 bar)。
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 ...)
但我不知道如何为
bar
中的每一行制作 foo
中关联行的副本,并将 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
。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 f.x, f.y, bar_id, b.z
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 a.x, a.y, b.bar_id
FROM a
JOIN b USING (z);
这应该执行您上次更新所描述的操作。
查询假设
z
是 UNIQUE
。如果 z
不唯一,它就会变得更加复杂。请参阅此相关答案中的查询 2,了解在这种情况下使用窗口函数 row_number()
的现成解决方案。
此外,请考虑用单个联合表替换 foo
和
bar
之间的 1:1 关系。
更多信息后的第二个答案。
如果您想在单个查询中向
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
一起返回,然后将 that 插入到 foo
中。您也可以使用任何其他数据。
基础知识
基本形式是:
INSERT INTO foo (...)
SELECT ... FROM foo WHERE ...
无需括号。
你可以对任何桌子做同样的事情
INSERT INTO foo (...)
SELECT ... FROM bar WHERE ...
您可以加入到在 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
- 可以包括您要插入的表。首先读取行,然后插入。
id
的
bar
是串行的并且有默认值
nextval('bar_id_seq'::regclass)
你可以手动调用这个函数来获取cte中的新id
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 f.x, f.y, b.new_id
FROM s_foo f
JOIN s_bar b on b.id = f.bar_id
)
SELECT 1