简体   繁体   English

是否可以在 postgres 中捕获已创建行的 ID 以在同一查询中使用?

[英]Is it possible to capture the ID of a created row in postgres to use in the same query?

TL;DR, I'm wondering if it's possible in postgres to create one resource, then create another resource that references it, all within the same statement. TL; DR,我想知道是否可以在 postgres 中创建一个资源,然后创建另一个引用它的资源,所有这些都在同一个语句中。 I'd like to do this in order to reduce round-trip times and simplify my backend logic.我想这样做是为了减少往返时间并简化我的后端逻辑。

I currently have two resources, foo and bar.我目前有两个资源,foo 和 bar。 Bar is a child resource of foo that references it: Bar 是引用它的 foo 的子资源:

CREATE TABLE foos (
    id serial PRIMARY KEY,
    name VARCHAR ( 50 ) UNIQUE NOT NULL
);

CREATE TABLE bars (
    id serial PRIMARY KEY,
    name VARCHAR ( 50 ) NOT NULL,
    description VARCHAR ( 50 ),
    foo_id int NOT NULL,
    FOREIGN KEY (foo_id)
      REFERENCES foos (id),
    UNIQUE (foo_id, name) 
);

I currently have the following sql statement to upsert a foo and bar at an endpoint v1/foos/{foo_name}/bars/{bar_name} :我目前有以下 sql 语句在端点v1/foos/{foo_name}/bars/{bar_name}上插入 foo 和 bar:

WITH upsert_foo AS
    (
        INSERT INTO "foos" 
        ("name")
        VALUES 
        ($1)
        ON CONFLICT ("name") DO NOTHING
    ),
     upsert_bar AS
      (
         INSERT INTO "bars" 
         ("name", "description", "foo_id" )
         VALUES 
         ($2, $3,   
            (SELECT "id" FROM "foos" WHERE "name" = $1) )
        ON CONFLICT ("name", "foo_id") DO 
        UPDATE SET
                 "description" = EXCLUDED."description",
            RETURNING *
    )
    SELECT upsert_bar.*;

When a user upserts a bar, a foo with the name from the URI should be created if it doesn't already exist, then the bar should be created if it doesn't exist.当用户 upsert 一个 bar 时,如果它不存在,则应创建一个名称来自 URI 的 foo,如果它不存在则应创建该 bar。 Otherwise, the bar should be updated.否则,应该更新栏。

However, this statement only works in the update case, when the foo resource already exists.但是,此语句仅适用于更新情况,即 foo 资源已经存在。 It seems that the new foo isn't showing up in (SELECT "id" FROM "foos" WHERE "name" = $1) when it's newly created.新创建的 foo 似乎没有出现在(SELECT "id" FROM "foos" WHERE "name" = $1)中。

Is there a way that I can capture the ID of the foo that was created (or already exists) and use it when I create the bar object?有没有办法可以捕获已创建(或已存在)的 foo 的 ID 并在创建栏 object 时使用它?

The only I can think to do is is break it down into separate operations:我唯一能想到的就是将其分解为单独的操作:

INSERT INTO 
   "foos" ("name")
VALUES 
    ($1)
ON CONFLICT ("name") DO UPDATE 
    SET name = foos.name
RETURNING id;

The above has a throw away UPDATE for purpose of capturing id value.上面有一个丢弃的UPDATE用于捕获id值。 Save this value to variable for use as $4 below.将此值保存到变量中以用作下面的$4

Then:然后:

INSERT INTO 
   "bars" ("name", "description", "foo_id" )
VALUES 
   ($2, $3, $4)
ON CONFLICT ("name", "foo_id") DO UPDATE 
    SET "description" = EXCLUDED."description"
RETURNING *

I managed to get this working in a single statement, but it was a bit complicated:我设法在一个语句中完成了这项工作,但这有点复杂:

WITH insert_foo AS
(
    INSERT INTO "foos" 
    ("name")
    VALUES 
    ($1)
    ON CONFLICT ("name") DO NOTHING RETURNING *
),
upsert_foo AS
(
    SELECT * FROM insert_foo
    UNION
    SELECT * FROM foos WHERE "name"=$1
),
upsert_bar AS
  (
     INSERT INTO "bars" 
     ("name", "description", "foo_id" )
     VALUES 
     ($2, $3, (SELECT "id" FROM upsert_foo WHERE name=$1) )
    ON CONFLICT ("name", "foo_id") DO 
    UPDATE SET
             "description" = EXCLUDED."description"
    RETURNING *
)
SELECT * FROM upsert_bar;

Basically, the result from the insert should be unioned with a regular select query, with only one of them containing a row, depending on whether the foo already existed or not.基本上,插入的结果应该与常规的 select 查询联合,其中只有一个包含一行,具体取决于 foo 是否已经存在。 I can then select the id from the result of this union query.然后我可以 select 来自此联合查询结果的 id。

I want to make a huge shout out to Adrian Klaver for pointing me in the right direction and also this answer: https://stackoverflow.com/a/62205017/4600258我想向 Adrian Klaver 大声喊叫,因为他指出了我正确的方向以及这个答案: https://stackoverflow.com/a/62205017/4600258

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM