使用postgres触发器有效地更新标签计数

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

我有一个架构,其中用户具有与之关联的标签:

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的计数不会改变,carrotjelly为递减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$;

这只是伪代码,因为我不确定如何整合我正在处理文本数组的事实,也不知道如何处理差异情况。感谢您的阅读。

sql postgresql triggers database-trigger tagging
2个回答
1
投票

您在数据中没有任何叫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

0
投票

我不确定您是否确实需要为此触发一个复杂的逻辑。您可以通过利用Postgres' array function unnest()

的简单查询来获得所需的输出。
unnest()

[请注意,如果您发现自己反复运行此类查询,则表明您的设计确实不适合您的用例;您最好将每个select x.tag, count(*) from mytable t cross join lateral unnest(t.tags) x(tag) group by x.tag 元组存储在单独的表行中,并与存储用户的表分开。

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