我的应用程序中有一个列表;
tags = ["nature", "science", "funny", "politics", "news"]
,我想检查所有这些元素是否存在于我的Tag.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; $$
答案 0 :(得分:1)
不需要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时才返回true。
您收到的错误是因为{'nature', 'politics'}
是无效的数组文字。您要么需要使用array
构造函数
array['nature', 'politics']
或可以转换为数组的字符串文字
'{nature, politics}'::text[]
(请注意,大括号位于字符串的内部)。
我更喜欢数组构造函数,因为我不必担心嵌套字符串文字。
想法是用户无法添加系统中尚未存在的任何新标签
此问题的正确解决方案是,使一个表包含标签定义,并确保每个标签名称仅使用一次:
create table tag_definition
(
name varchar(50) primary key
);
然后在您的标签表中引用tag_definition:
create table tags
(
name varchar(50) not null references tag_definition,
post_id integer not null references posts
);
现在无法在tags
表中插入不存在的标记。
现在您要做的就是在插入行时捕获异常。 插入之前,无需检查标签。
通过为tags
表(例如tag_definition
)使用生成的主键,并将serial
表用作参考,您可以节省空间并使tags
表变小{1}}表。
鉴于您的问题中的插入语句,您可以使用单个插入语句来实现相同的目的:
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返回所有内容。如果至少不存在一个标签,则不会插入任何内容。
要使(某种程度上)有效,您将需要在`tags(name)上建立索引。