【问题标题】:Outer SELECT from INSERT RETURNING and inner SELECT statement来自 INSERT RETURNING 的外部 SELECT 和内部 SELECT 语句
【发布时间】:2016-10-30 12:37:57
【问题描述】:

我正在尝试编写一个查询,将表中的一行复制到同一个表中,给它一个新的顺序主键,并将它与一个新的外键相关联。我需要将新的主键与另一个未插入且存在于不同关系表(查找表)中的外键相关联。

我希望能够将其作为单个事务执行,但我似乎找不到将原始行与复制行相关联的方法,因为复制行的唯一 ID 是新的。这会有点拗口,但这是我的具体问题:

外部 SELECT 子句能否将内部 INSERT 与内部 SELECT 子句和 RETURNING 括起来,以便选择并正确连接来自内部 SELECT 和 INSERT 的 RETURNING 子句的值?这是我尝试过的:

WITH batch_select AS (
    SELECT id, owner_id, 1992 AS project_id
    FROM batch
    WHERE project_id = 1921
),
batch_insert AS (
    INSERT INTO batch (owner_id, project_id)
    SELECT bs.owner_id, bs.experiment_id
    FROM batch_select bs
    RETURNING id
)
SELECT bs.id AS origin_id, bi.id AS destination_id
FROM batch_select bs, batch_insert bi;

我需要origin_id 对应destination_id。显然,现在它只是一个 CROSS JOIN,其中所有内容都与所有内容配对并且不是很有用。我还将使用最后一个 SELECT 语句的结果将 INSERT 运行到查找表中,类似这样(batch_join_select 查询可以在最后一个插入中实现,但为了清楚起见保留了):

WITH batch_select AS (
    SELECT id, owner_id, 1992 AS project_id
    FROM batch
    WHERE project_id = 1921
),
batch_insert AS (
    INSERT INTO batch (owner_id, project_id)
    SELECT bs.owner_id, bs.experiment_id
    FROM batch_select bs
    RETURNING id
),
batch_join_select AS (
    SELECT bs.id AS origin_id, bi.id AS destination_id
    FROM batch_select bs, batch_insert bi
)
INSERT INTO lookup_batch_container (batch_id, container_id)
SELECT bjs.destination_id, lbc.container_id
FROM batch_join_select bjs
INNER JOIN lookup_batch_container lbc ON lbc.batch_id = bjs.origin_id;

我在 dba 交换中找到了一个类似的question,但是当有不止一行时,接受的答案不能正确地将两者关联起来。

我是否只需要处理几笔交易?

[编辑]添加一些最小架构:

Table lookup_batch_container
    Column    |  Type   | Modifiers 
--------------+---------+-----------
 batch_id     | integer | not null
 container_id | integer | not null

Indexes:
    "lookup_batch_container_batch_id_container_id_key" UNIQUE CONSTRAINT, btree (batch_id, container_id)
Foreign-key constraints:
    "lookup_batch_container_batch_id_fkey" FOREIGN KEY (batch_id) REFERENCES batch(id) ON DELETE CASCADE
    "lookup_batch_container_container_id_fkey" FOREIGN KEY (container_id) REFERENCES container(id) ON DELETE CASCADE

Table batch
      Column      |            Type             |                                     Modifiers                                      
------------------+-----------------------------+------------------------------------------------------------------------------------
 id               | integer                     | not null default nextval('batch_id_seq'::regclass)
 owner_id         | integer                     | not null
 project_id       | integer                     | not null

Indexes:
    "batch_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "batch_project_id_fkey" FOREIGN KEY (project_id) REFERENCES project(id) ON DELETE CASCADE
    "batch_owner_id_fkey" FOREIGN KEY (owner_id) REFERENCES owner(id) ON DELETE CASCADE
Referenced by:
    TABLE "lookup_batch_container" CONSTRAINT "lookup_batch_container_batch_id_fkey" FOREIGN KEY (batch_id) REFERENCES batch(id) ON DELETE CASCADE

Table container
        Column         |            Type             |                                  Modifiers                                   
-----------------------+-----------------------------+------------------------------------------------------------------------------
 id                    | integer                     | not null default nextval('stirplate_source_file_container_id_seq'::regclass)
 owner_id              | integer                     | not null
 status                | container_status_enum       | not null default 'new'::container_status_enum
 name                  | text                        | not null

Indexes:
    "container_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "container_owner_id_fkey" FOREIGN KEY (owner_id) REFERENCES owner(id) ON DELETE CASCADE
Referenced by:
    TABLE "lookup_batch_container" CONSTRAINT "lookup_batch_container_container_id_fkey" FOREIGN KEY (container_id) REFERENCES container(id) ON DELETE CASCADE

【问题讨论】:

  • 您能在batch 表中添加origin_id 列吗?
  • @ClodoaldoNeto 我想这可行。它不会用于其他任何令人遗憾的事情。它可能还需要对我们的 API 的某些部分进行一些重构,而其他程序员懒得在查询中不使用 *。继续希望有一些神奇的 sql,但现在感谢您的选择。
  • 在第一个查询中,1992 project_id 是新的吗?如果是这样,我可以让它工作。但我不确定我是否理解 lookup table 的目的。为什么不发布表格定义?
  • @ClodoaldoNeto 是的,1992 是新的 project_id,它们被复制到(真正关联)。我在上面添加了一些表定义,请注意所有这些都被截断了。

标签: database postgresql select insert copy


【解决方案1】:
with batch_select as (
    select id, owner_id, 1992 as project_id
    from batch
    where project_id = 1921
), batch_insert as (
    insert into batch (owner_id, project_id)
    select owner_id, project_id
    from batch_select
    order by id
    returning *
)
select unnest(oid) as oid, unnest(did) as did
from (
    select
        array_agg(distinct bs.id order by bs.id) as oid,
        array_agg(distinct bi.id order by bi.id) as did
    from
        batch_select bs
        inner join
        batch_insert bi using (owner_id, project_id)
) s
;
 oid | did 
-----+-----
   1 |   4
   2 |   5
   3 |   6

鉴于此batch 表:

create table batch (
    id serial primary key,
    owner_id integer,
    project_id integer
);
insert into batch (owner_id, project_id) values
(1,1921),(1,1921),(2,1921);

如果主键是(id, owner_id, project_id) 会更简单。不是吗?

【讨论】:

  • 我很快就会试试这个代码(我昨天用 3 笔交易实现了 - 我仍然不喜欢 - 我会换掉它),看起来很有希望谢谢你的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-31
  • 2018-08-18
  • 2010-12-30
  • 1970-01-01
  • 2011-03-04
  • 2011-12-04
  • 1970-01-01
相关资源
最近更新 更多