如何根据输入数组插入多行标签?

问题描述 投票:0回答:1

我有一个帖子表、一个主题标签表以及一个将帖子链接到主题标签的表,如下所示:

CREATE TABLE posts(
id SERIAL PRIMARY KEY,
post_data VARCHAR(128) NOT NULL
);

CREATE TABLE hashtags (
id SERIAL PRIMARY KEY,
value VARCHAR(128) NOT NULL
);

CREATE TABLE post_hashtags(
id SERIAL PRIMARY KEY,
post_id INTEGER NOT NULL REFERENCES posts(id),
hashtag_id INTEGER NOT NULL REFERENCES hashtag(id)
);

INSERT INTO posts(post_data) VALUES ('post1');
INSERT INTO hashtags (value) VALUES ('hashtag1'), ('hashtag2'), ('hashtag3');

-- At this point I might want to add links between post1 and existing hashtags as well as possibly new ones

每当用户使用多个哈希标签发表帖子时,我想:

  1. posts
    中创建一个新行并获取ID
  2. hashtags
    中为不存在的主题标签创建新行并获取其 ID
  3. 对于每个主题标签,在
    post_hashtags
    中使用帖子和主题标签 ID 创建一行

现在,我能够在服务器端处理它,但这显然是糟糕的性能:插入帖子并获取 ID;对于每个主题标签,插入

hashtags
,如果不存在,获取ID;然后插入
post_hashtags
。我猜想可以简化对数据库的大量调用,但目前我的 SQL 技能还缺乏。

postgresql insert many-to-many common-table-expression upsert
1个回答
0
投票

您可以使用数据修改 CTE 通过单个查询安全高效地完成此操作。

当然,您需要对

UNIQUE
进行
PRIMARY KEY
hashtags (value)
约束。 (你应该在
post_hashtags (post_id, hashtag_id)
上拥有一个。)

WITH input(post_data, tags) AS (  -- provide single data row with array of tags
   VALUES ('post2', '{hashtag1, hashtag2, hashtag4}'::text[])  -- single post!
   )
, tag_set AS (  -- unnest tags - may be empty/missing (?)
   SELECT unnest(i.tags) AS value
   FROM   input i
   )
, ins_p AS (
   INSERT INTO posts (post_data)
   SELECT i.post_data
   FROM   input i
   RETURNING id AS post_id
   )
, ins_h AS (
   INSERT INTO hashtags (value)
   SELECT t.value
   FROM   tag_set t
   WHERE  NOT EXISTS (SELECT FROM hashtags h WHERE h.value = t.value)  -- optional to avoid burning lots of serial IDs
   ON CONFLICT (value) DO NOTHING
   RETURNING id AS hashtag_id
   )
INSERT INTO post_hashtags
      (  post_id,   hashtag_id)
SELECT p.post_id, t.hashtag_id
FROM   ins_p p
CROSS  JOIN (  -- only if actual tags were entered
   TABLE ins_h  -- new tags
   UNION  ALL
   SELECT h.id AS hastag_id  -- pre-existing tags
   FROM   tag_set  t
   JOIN   hashtags h USING (value)
   ) t
RETURNING *;

小提琴

即使在并发写入负载很重的情况下,这也是安全的。
仍然存在两种可能的极端情况

  1. 并发事务可能会创建相同的新主题标签,但随后回滚,导致表

    hashtags
    中缺少该查询的条目,从而引发异常。

  2. 预先存在的主题标签可能会被在此处查找和插入到

    post_hashtags
    之间的并发事务删除。再次提出例外。极不可能,但有可能。

如果出现这两个不太可能出现的问题之一,您可以重新运行此查询。
你们在同一个查询中涵盖了这两个方面。我懒得走那么远。请参阅此处的说明和详细说明:

© www.soinside.com 2019 - 2024. All rights reserved.