Как вставить много строк тегов на основе входного массива?

У меня есть таблица сообщений, таблица хэштегов и таблица, связывающая сообщения с хэштегами, например:

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 и получите идентификатор.
  2. Создайте новые строки в hashtags для несуществующих хэштегов и получите их идентификаторы.
  3. Для каждого хэштега создайте строку post_hashtags с идентификаторами публикации и хэштега.

Прямо сейчас я могу справиться с этим на стороне сервера, но это, очевидно, ужасная производительность: вставить сообщение и получить идентификатор; для каждого хештега вставьте в hashtags, если его нет, получите идентификатор; затем вставьте в post_hashtags. Множество вызовов к БД, которые, я думаю, можно было бы упростить, но на данный момент мне не хватает навыков SQL.

По крайней мере, предоставьте правильные определения таблиц, вашу версию Postgres и образец ввода. Кроме того, все, что было до сих пор, даже если это не работает.

Erwin Brandstetter 23.06.2024 07:30

Хорошо, я добавил код SQL, который можно запустить для настройки состояния. Меня будет интересовать любой запрос, независимо от версии Postgres. Как я уже упоминал в посте, у меня есть только серверный код, но пока нет SQL.

learnandgrow 23.06.2024 14:44

Столбец id в post_hashtags не обязателен; однако для (post_id, hashtag_id) должно быть уникальное ограничение, чтобы исключить избыточные строки.

JohnH 23.06.2024 20:30
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
3
62
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете сделать это безопасно и эффективно с помощью одного запроса, используя изменяющие данные CTE и UPSERT для тегов.

Конечно, вам нужно ограничение 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. Опять же, вызывая исключение. Крайне маловероятно, но возможно.

Вы можете просто повторно запустить этот запрос, если возникнет одна из этих двух маловероятных проблем.
Или вы охватываете оба крайних случая в одном запросе. Я не стал заходить так далеко. Инструкции и подробное объяснение смотрите здесь:

Связанный:

Другие вопросы по теме