就像@a_horse一样:使用规范化的设计会更简单(除了使其他任务更简单/更简洁),但
仍然不简单 。
另外,数据类型的PK列
charactervarying(36)高度可疑(且效率低下),并且很可能应该是
integer类型,或者至少应该是类型
uuid。
这是根据您的 设计的 一种可能的解决方案 ,如下所示 :
WITH cte AS ( SELECt id, string_to_array(a.keywords, ',') AS keys FROM article a )SELECt id, string_agg(b_id, ',') AS best_matchesFROM ( SELECt a.id, b.id AS b_id , row_number() OVER (PARTITION BY a.id ORDER BY ct.ct DESC, b.id) AS rn FROM cte a LEFT JOIN cte b ON a.id <> b.id AND a.keys && b.keys LEFT JOIN LATERAL ( SELECt count(*) AS ct FROM ( SELECt * FROM unnest(a.keys) INTERSECT ALL SELECt * FROM unnest(b.keys) ) i ) ct ON TRUE ORDER BY a.id, ct.ct DESC, b.id -- b.id as tiebreaker ) subWHERe rn < 4GROUP BY 1;
SQL Fiddle (
id改为使用整数)。
CTE
cte将字符串转换为数组。您甚至可以拥有像这样的功能性GIN索引…
如果在前3个选择中有多行并列,则需要定义一个 _ 决胜局_ 。在我的示例中,较小的行排在
id第一位。
比较是在JSON数组和SQL数组之间进行的,但它基本上是相同的问题,需要解决相同的问题。还比较了两个类似的选择。
为了加快速度,您至少应该在数组列上有一个GIN索引(而不是用逗号分隔的字符串),并且查询不需要CTE步骤。完全归一化的设计还有其他优点,但不一定比具有GIN索引的数组更快。



