sql - 在单个查询中将行插入多个表,从涉及的表中选择

标签 sql postgresql insert common-table-expression

我有以下形式的两个表(即,每个 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);

这应该按照您上次更新的描述执行。

查询假定 zUNIQUE。如果 z 不是唯一的,它会变得更复杂。引用Query 2 in this related answer在这种情况下,使用窗口函数 row_number() 获得现成的解决方案。

此外,考虑用单个联合表替换 foobar 之间的1:1 关系


数据修改CTE

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

如果你想在单个查询中向 foo bar 添加行,你可以使用 data modifying CTE从 PostgreSQL 9.1 开始:

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。您也可以使用任何其他数据。

这是一个working demo to play with on sqlfiddle .


基础知识

在澄清之前包含基本信息的原始答案。
基本形式是:

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 和其他任何东西一样 - 可以包括您要插入的表。首先读取行,然后插入行。

关于sql - 在单个查询中将行插入多个表,从涉及的表中选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10471757/

相关文章:

php - mysql 数据库的更新逻辑问题

sql - 关于 "reverse LIKEs"的MySQL问题

ruby-on-rails - 使用docker-compose在执行第一个命令成功后,执行 `rake db:setup`的docker容器中的Rails 5始终会断开与postgresql的连接

postgresql - "LIKE"没有按预期工作

php - 通过 PHP 在 SQL 的 TEXT 字段中插入 ' or "

python - SQLAlchemy 急切加载多个关系

ruby-on-rails - ActiveRecord::StatementInvalid: PG::DatetimeFieldOverflow: 错误:日期/时间字段值超出范围: "22/11/2019 19:47:22.793483"

c - 链表: Insert function points to itself after second insertion

database - 插入时检查值

mysql - 为什么说这个触发器包含sql语法错误?