我正在尝试为Postgres写一个查询,以便在一个表中插入一行,而副作用是,该表可能在第二个表中插入一行(由于第一个表在第二个表上有外键约束)。
我想我可以作为两个单独的查询来执行此操作,可能在一个事务中,但是现在我已经开始了单个查询的路径,如果可能的话,我想知道如何完成所有。
我一直在尝试使用How to use RETURNING with ON CONFLICT in PostgreSQL?和Insert if names do not exist and return ids if exist中列出的一般结构,这似乎是一个合理的起点,但是我遇到了错误WITH clause containing a data-modifying statement must be at the top level
,并且我不确定如何遵循该建议。
表a
如下所示:
CREATE TABLE a (
a_uuid uuid PRIMARY KEY,
b_uuid uuid REFERENCES b(b_uuid)
)
表b
是:
CREATE TABLE b (
b_uuid uuid PRIMARY KEY,
b_name text UNIQUE
)
我想在a
中插入一行,其中b_uuid
是根据匹配b_name
的输入来计算的。如果b
已经有该行,则使用相应的b_uuid
;否则,在b
中用该文本和一个新生成的UUID创建新行,并返回后者以用作a
中插入的一部分。这是我在意识到自己不知所措之前写的:
WITH new_b AS (
WITH input (b_uuid, b_name) AS (
VALUES (
gen_random_uuid(), $1
)
), ins AS (
INSERT INTO b (
b_uuid, b_name
)
TABLE input
ON CONFLICT DO NOTHING
RETURNING b_uuid
)
TABLE ins
UNION ALL
SELECT b_uuid FROM input
)
INSERT INTO a (a_uuid, b_uuid)
VALUES (
gen_random_uuid(), (SELECT b_uuid FROM new_b)
)
我在附近吗?最好的方法是什么?
答案 0 :(得分:1)
demo:db<>fiddle(由于随机事物,如果随机uuid等于1,您可能会重新加载几次;而不是我使用uuid
的类型int
,因为小提琴引擎当前不支持pgcrypto
扩展。我用自己的功能模拟了该功能。)
WITH input (b_uuid, b_name) AS (
VALUES (
gen_random_uuid(), $1
)
), ins_b AS (
INSERT INTO b (
b_uuid, b_name
)
TABLE input
ON CONFLICT DO NOTHING
RETURNING b_uuid
), new_b AS (
TABLE ins_b
UNION ALL
SELECT b.b_uuid FROM input
JOIN b USING (b_uuid)
)
INSERT INTO a (a_uuid, b_uuid)
VALUES (
gen_random_uuid(), (SELECT b_uuid FROM new_b)
);
您的解决方案并不遥远:
INSERT
)不能放在嵌套的WITH
子句中(此时:谢谢,我什至不知道嵌套的CTE功能:D)JOIN
您的解决方案缺失:
new_b
部分的工作方式如下:如果存在冲突,ins_b
不返回任何内容。因此,TABLE ins_b
为空。在这种情况下,需要直接从b_uuid
调用已经存在的TABLE b
。拿出生成的UUID,将其与b
结合会给出现有的b_uuid
(并且,如果需要,此记录的所有其他列)。但是,如果没有冲突-b_uuid
还不存在-那么ins_b
返回新数据集,TABLE ins_b
不为空,但是原始表上的联接失败,因为仍然没有可用于加入的记录。
当然,这可以插入多个记录。