Как проверить, существуют ли элементы списка в таблице postgres - PullRequest
0 голосов
/ 24 сентября 2018

У меня в приложении есть список:

tags = ["nature", "science", "funny", "politics", "news"]

, и я хочу проверить, существуют ли все эти элементы в моем поле таблицы Tags.name.Идея заключается в том, что пользователь не может добавить новый тег, которого еще нет в системе.

В настоящее время я пытаюсь запустить цикл .foreach в своем запросе как;

DO $$
BEGIN
  FOREACH itag IN ARRAY {'nature', 'politics'} LOOP
    IF EXISTS (SELECT 1 FROM tags WHERE name = itag) THEN
      INSERT INTO TAGS (name, post_id) values itag, 'post01' ;
    ELSE
      RAISE EXCEPTION 'Tag % doesnt exists in table', itag;
    END IF;
  END LOOP;
END; $$

это выдает ошибку как;

ERROR:  syntax error at or near "{"
LINE 3:   FOREACH itag SLICE 1 IN ARRAY {'nature', 'politics'} LOOP

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

Также, если возможно, есть ли способ оптимизировать мой запрос?


РЕДАКТИРОВАТЬ: Я использовалОтвет @a_horse_with_no_name на создание потока, похожего на то, что я ищу.Если все теги присутствуют в моей таблице, я добавлю эти записи, иначе я сгенерирую исключение.

DO $$
BEGIN
 IF (with to_check (itag) as (
   values ('nature'),('science'),('politics'),('scary')
  )
  select bool_and(exists (select * from tags t where t.name = tc.itag)) as all_tags_present
  from to_check tc) THEN
  RAISE INFO 'ALL GOOD. Here I will add the insert statement in my app';
ELSE
  RAISE EXCEPTION 'One or more tags are not present';
END IF;

END; $$

1 Ответ

0 голосов
/ 24 сентября 2018

Нет необходимости в PL / pgSQL или цикле.Вы можете использовать список тегов и проверять существование каждого из них в одном выражении:

with to_check (itag) as (
   values ('nature'),('science'),('funny'),('politics'),('news')
)
select tc.itag, 
       exists (select * from tags t where t.name = tc.itag) as tag_exists
from to_check tc;

Если вы просто хотите, чтобы один флаг сообщал вам, если хотя бы один тег отсутствует, выможно использовать следующее:

with to_check (itag) as (
   values ('nature'),('science'),('funny'),('politics'),('news')
)
select bool_and(exists (select * from tags t where t.name = tc.itag)) as all_tags_present
from to_check tc;

bool_and вернет true только если все значения верны.


Вы получаете ошибку, потому что {'nature', 'politics'} является недопустимым литералом массива.Вам нужно либо использовать конструктор array

array['nature', 'politics'] 

, либо строковый литерал, который можно привести к массиву

'{nature, politics}'::text[]

(обратите внимание, что фигурные скобки внутри строки).

Я предпочитаю конструктор массива, так как мне не нужно беспокоиться о вложенности строковых литералов.


Идея состоит в том, что пользователь не может добавить новый тег, которыйотсутствует в системе

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

create table tag_definition
(
   name   varchar(50) primary key
);

Затем в вашей таблице тегов укажите тег_определения:

create table tags
(
   name     varchar(50) not null references tag_definition, 
   post_id  integer  not null references posts
);

Теперь невозможно вставить несуществующий тег в таблицу tags.

Все, что вам нужно сделать сейчас, это перехватить исключение при вставке строк.Нет необходимости проверять теги перед вставкой .

Вы можете сэкономить место и существенно уменьшить таблицу tags, используя сгенерированный первичный ключ для таблицы tag_definition (например,serial) и используйте это для справки в таблице tags.


Учитывая оператор вставки в вашем вопросе, вы могли бы добиться того же с помощью одного оператора вставки:

with to_check (itag) as (
   values ('nature'),('politics')
)
insert into tags (tag, post_id)
select tc.itag, 'post01'
from to_check tc 
where not exists (select itag
                  from to_check
                  except
                  select t.name
                  from tags t);

Однако это не очень хорошо масштабируется при увеличении таблицы тегов,Подвыбор ничего не вернет, если все теги из to_check существуют, и, следовательно, условие not exists заставит INSERT вернуть все.Если хотя бы один тег не существует, ничего не будет вставлено.

Чтобы сделать это (несколько) эффективным, вам понадобится индекс `тэгов (имя).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...