【问题标题】:Insert several thousand rows into table with values from other tables将数千行插入包含其他表中的值的表中
【发布时间】:2021-04-04 00:13:55
【问题描述】:

我有一个 PostgreSQL 数据库,其中包含 4 个表 - 表 A、B、C 和 D。表 A 有三列,它们是其他三个表的 ID。

类似这样的:

TABLE A
--------------------------
| id | B_id | C_id | D_id |
---------------------------

我已经在表 B、C、D 中有几千行数据,现在想通过根据某些条件从其他 3 个表中选择数据来为表 A 生成一个插入脚本。

我尝试如下:

INSERT INTO A (B_id, C_id, D_id) VALUES ((SELECT id FROM B WHERE CONDITION), (SELECT id FROM C WHERE CONDITION), (SELECT id FROM D WHERE CONDITION)),((SELECT id FROM B WHERE CONDITION), (SELECT id FROM C WHERE CONDITION), (SELECT id FROM D WHERE CONDITION)),((SELECT id FROM B WHERE CONDITION), (SELECT id FROM C WHERE CONDITION), (SELECT id FROM D WHERE CONDITION)), ....;

但是,对于大量行,这需要很长时间然后失败。

我想知道我这样做是否正确,如果不正确,实现我想要的最有效的方法是什么。

更详细的信息和示例数据

Accounts
---------------------------------------------
| id | firstName | lastName | email (unique)|
---------------------------------------------
  1     Account      One      aone@email.com

Groups
----------------------
| id | name (unique) |
----------------------
  1      Group One
Titles
----------------------
| id | name (unique) |
----------------------
  1      Title One

然后表 A 包含来自上述每个的 id:

A
------------------------------------
| id | accountId | groupId | titleId
------------------------------------
  1        1          1          1

对于每个帐户(每个唯一的电子邮件),我以编程方式生成插入语句

INSERT INTO A (accountId, groupId, titleId)
VALUES (
    (SELECT id FROM accounts WHERE email = {EMAIL}),
    (SELECT id FROM groups WHERE name = {groupName}),
    (SELECT id FROM titles WHERE name = {titleName}))

如前所述,将生成数千个(大约 15000 个)插入语句。条件值是通过我的脚本中的一些解析数据以编程方式添加的。

select 语句将始终只返回一行,因为 where 条件是在具有唯一约束的列上完成的。因此,每封帐户电子邮件都应在表 A 中具有帐户 + 组 + 标题的一种组合。

【问题讨论】:

  • VALUES 子句中的子查询在逻辑上没有明确定义,因为我们甚至不知道它们是否都会生成相同数量的记录。请为表格添加示例数据。
  • 样本数据、期望的结果以及对逻辑的更好解释都会有所帮助。
  • 我在 OP 中添加了一些额外的信息。
  • A上是否有任何索引/触发器?
  • 没有索引或触发器

标签: sql postgresql sql-insert


【解决方案1】:

您的值表达式设置为准确插入 一个 行。每个子查询都是一个标量子查询,最多可以返回一行。如果其中任何一个返回多于一行,则整个查询将失败。

也许您想要所有子查询返回的值的笛卡尔积。如果是这样,请使用insert . . . select

INSERT INTO A (B_id, C_id, D_id) 
    SELECT b.id, c.id, d.id
    FROM (SELECT id FROM B WHERE CONDITION) b CROSS JOIN
         (SELECT id FROM C WHERE CONDITION) c CROSS JOIN
         (SELECT id FROM D WHERE CONDITION) d;

首先运行SELECT 以确保它生成您要插入的行。

【讨论】:

  • select 语句将始终只返回一行。我在 OP 中添加了一些额外的信息。
  • @rok 。 . .您的问题的标题另有说明,这就是我回答的原因。
  • 是的,如果标题有点混乱,我很抱歉。希望我的帖子更新解释得足够好。
【解决方案2】:

为所有{EMAIL}, {groupName}, {titleName} 三元组创建一个临时表并填充它。可以说是:

create temp table filler (
    email text,
    group_name text,
    title_name text);

insert into filler values
    ('email1', 'group1', 'title1'),
    ('email2', 'group2', 'title2'),
    ....
    ('emailn', 'groupn', 'titlen');

或使用copy

然后使用它在单个语句中插入值:

insert into A (accountId, groupId, titleId)
select a.id, g.id, t.id
from
    filler as f
        left join accounts as a on (f.email = a.email)
        left join groups as g on (f.group_name = g.name)
        left join titles as t on (f.title_name = t.name);

PS:accounts.emailgroups.nametitles.name 上的索引可能很有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-17
    • 1970-01-01
    • 2011-05-07
    • 2023-04-06
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多