我有一个架构,其中用户具有与之关联的标签:
user_id: int4 tags: text[]
------------- ------------
1 [ 'apple', 'carrot', 'jelly' ]
2 [ 'jelly', 'zebra' ]
我正在寻找创建可返回所有标签及其关联计数的列表的查询。示例:
tag: text count: int4
--------- -----------
'apple' 1
'carrot' 1
'jelly' 2
'zebra' 1
触发程序似乎是执行此操作的理想方法,因为该应用程序是重读和轻写的。
但是,我很难实现这一点。我认为触发器本身似乎很简单:
CREATE TRIGGER tags_count_update
AFTER UPDATE OF tags ON person
FOR EACH ROW
EXECUTE PROCEDURE trigger_update_tags_count();
trigger_update_tags_count
是我遇到麻烦的部分,因为它似乎是一个非常复杂的操作。例如,如果tags
表中尚不存在该标记,则应插入计数为1的标记。
[另外,我相信您需要执行某种差异操作,因为如果某人的标签从[ 'apple', 'carrot', 'jelly' ]
更新为[ 'apple', 'dogs' ]
,则apple
的计数不会改变,carrot
和jelly
为递减1,然后创建dogs
,其计数设置为1。这与标记是数组这一事实更加复杂。我目前有这样的东西:
CREATE OR REPLACE FUNCTION public.trigger_update_tags_count()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
INSERT INTO tags (tag, count) VALUES (new.tag, 1)
ON CONFLICT (tag) DO UPDATE
SET count = CASE WHEN (tag.new AND NOT tag.old) THEN count + 1
WHEN (tag.old AND NOT tag.new) THEN count - 1
ELSE count
END
RETURN NEW;
END;
$function$;
这只是伪代码,因为我不确定如何整合我正在处理文本数组的事实,也不知道如何处理差异情况。感谢您的阅读。
您在数据中没有任何叫tag
的内容;它是tags
。因此,您需要UNNEST()
并同时处理新旧标签。我在想类似的东西:
INSERT INTO tags (tag, count)
SELECT COALESCE(ntag, otag),
(ntag IS NOT NULL)::int - (otag IS NOT NULL)::int
FROM UNNEST(new.tags) ntag FULL JOIN
UNNEST(old.tags) otag
ON ntag = otag
ON CONFLICT (tag) DO UPDATE
SET count = count + (excluded.ntag IS NOT NULL)::int - (excluded.otag IS NOT NULL)::int
我不确定您是否确实需要为此触发一个复杂的逻辑。您可以通过利用Postgres' array function unnest()
:
unnest()
[请注意,如果您发现自己反复运行此类查询,则表明您的设计确实不适合您的用例;您最好将每个select x.tag, count(*)
from mytable t
cross join lateral unnest(t.tags) x(tag)
group by x.tag
元组存储在单独的表行中,并与存储用户的表分开。