我在我的PieCloudDB数据库中有两个表keywordsposts.

每个主题都可以用一个或多个关键字来表达.如果某个主题的关键字存在于帖子(case insensitive)的内容中,则该帖子具有该主题.

例如:

topic_id keyword
1 basketball
2 music
3 food
4 war
post_id content
1 A typhoon warning has been issued in southern Japan
2 We are going to play neither basketball nor volleyball
3 I am indulging in both the delightful music and delectable food
4 That basketball player fouled again

现在我想根据以下规则找到每个帖子的主题:

  • 如果文章没有任何主题的关键字,则其主题应为"Vague!".

  • 如果帖子至少有一个主题的关键字,则其主题应该是以升序排序并以逗号','分隔的主题ID的字符串.

对于上述示例数据,结果应为:

post_id topics
1 Vague!
2 1
3 2,3
4 1
SELECT post_id, COALESCE(array_to_string(array_agg(DISTINCT topic_id ORDER BY topic_id), ','), 'Vague!') AS topic
FROM (
    SELECT p.post_id, k.topic_id
    FROM Posts p 
    LEFT JOIN Keywords k 
    ON LOWER(content) LIKE '% ' || keyword || ' %' OR content LIKE keyword || ' %' OR content LIKE '% ' || keyword
  ) a
GROUP BY post_id
ORDER BY post_id

我try 了这个查询,但我得到的结果并不完全正确.我不知道为什么post 1的输出是null:

post_id topics
1
2 1
3 2,3
4 1

Can anyone give me a correct answer?
(If you don’t know the database I use, you can use PostgreSQL instead.)

推荐答案

不支持索引的简单正则表达式

没有索引支持(只有对琐碎的基数是合理的!)这是Postgr最快的,同时做exactly个你要求的:

SELECT post_id, COALESCE(string_agg(topic_id::text, ',' ), 'Vague!') AS topic
FROM  (
   SELECT p.post_id, k.topic_id
   FROM   post p 
   LEFT   JOIN keyword k ON p.content ~* ('\m' || k.keyword || '\M')
   ORDER  BY p.post_id, k.topic_id
   ) sub
GROUP  BY post_id
ORDER  BY post_id;

~*是不区分大小写的正则表达式匹配运算符.参见:

关于我的正则表达式,我引用the manual:

\m ... matches only at the beginning of a word
\M ... matches only at the end of a word

这隐含地覆盖了字符串的开始(^)和结束($).\W(如另一个答案中所建议的)匹配任何非单词字符,对于任务来说是错误的.

请注意我如何在子查询中应用一次ORDER BY而不是每个聚合,因为这样更快.参见:

在这个星座中,一个简单的COALESCE捕捉到没有匹配的情况.

严格匹配的FTS索引

上面的简单(朴素)方法以O(N * M)为尺度,也就是说,每个表中的行数非常不平凡.通常,您需要索引支持.

虽然严格匹配关键字,但最好的索引应该是100 with the 101 dictionary,以及一个可以实际使用该索引的查询:

CREATE INDEX post_content_fts_simple_idx ON post USING gin (to_tsvector('simple', content));


SELECT post_id, COALESCE(topics, 'Vague!') AS topics
FROM  (
   SELECT post_id, string_agg(topic_id::text, ',') AS topics
   FROM  (
      SELECT p.post_id, k.topic_id
      FROM   keyword k
      JOIN   post    p ON to_tsvector('simple', p.content) @@ to_tsquery('simple', k.keyword)
      ORDER  BY p.post_id, k.topic_id
      ) sub
   GROUP  BY post_id
   ) sub1
RIGHT  JOIN post p USING (post_id)
ORDER  BY post_id;

英文单词匹配FTS索引

要使用内置词干匹配自然语言单词,请在示例中使用匹配dictionary, 100:

CREATE INDEX post_content_fts_en_idx ON post USING gin (to_tsvector('english', content));

SELECT post_id, COALESCE(topics, 'Vague!') AS topics
FROM  (
   SELECT post_id, string_agg(topic_id::text, ',') AS topics
   FROM  (
      SELECT p.post_id, k.topic_id
      FROM   keyword k
      JOIN   post    p ON to_tsvector('english', p.content) @@ to_tsquery('english', k.keyword)
      ORDER  BY p.post_id, k.topic_id
      ) sub
   GROUP  BY post_id
   ) sub1
RIGHT  JOIN post p USING (post_id)
ORDER  BY post_id;

fiddle

对于模糊匹配,考虑三元索引.参见:

相关内容:

Sql相关问答推荐

Postgresql:从jsons数组到单个id索引的json

基于前面行的值:当x&>2时重复1,当连续3行x=0时则重复0

如何在联接条件不匹配时按日期获取上一条记录

当交叉联接3个或更多表时,实体框架中是否会传输冗余的行数据并占用数据库带宽?

更新其组的日期字段值小于最大日期减go 天数的记录

如何查找所提供日期范围的所有季度开始日期和结束日期

有没有办法在Postgres中存储带有时区的时间戳,而不将其转换为UTC

我需要一个regexp_like来只验证字母D或T、数字和管道

将JSON文件导入Postgres 16数据库时出错(22P04上次预期列之后的额外数据)

将用户授予另一个用户不授予权限

复制行并根据 Oracle SQL 中其他表的值更改值

特殊条件计算小计

snowflake中的动态文件名生成

在SQL中实现表格数据透视类型报表

如何为给定的股票数据集计算利润/亏损,确保先卖出先买入的股票

将表格和字符串连接以 for each 记录生成订单项目

将有效数字作为 varchar 返回的 SQL 函数

SQL Server 查找存在于所有不同时期(或序列)中的条目

使用 SQL 表中的连接列删除重复记录

删除具有相同 ID 的重复记录 - Postgresql