按公共数组元素的计数对结果进行排序

tim*_*one 6 postgresql array

使用 Postgres 9.4,我有兴趣拥有一个整数数组,例如user_ids_who_like并提供一个用户数组(例如user_ids_i_am_following)来对该交集进行排序。

就像是:

select * 
from items 
where [there is an intersection between 
       user_ids_who_like with user_ids_i_am_following] 
order by intersection(user_ids_who_like).count
Run Code Online (Sandbox Code Playgroud)

是否可以通过数组交集进行分组和排序?

示例数据:

items
name          | user_ids_who_like
'birds'       | '{1,3,5,8}'
'planes'      | '{2,3,4,11}'
'spaceships'  | '{3,4,6}'
Run Code Online (Sandbox Code Playgroud)

对于给定的user_ids_who_i_follow = [3,4,11],我可以执行以下操作:

select * from items
where <user_ids_who_like intersects with user_ids_who_i_follow>
order by <count of that intersection>
Run Code Online (Sandbox Code Playgroud)

想要的结果:

name          | user_ids_who_like  | count
'planes'      |  '{2,3,4,11}'      | 3
'spaceships'  |  '{3,4,6}'         | 2
'birds'       |  '{1,3,5,8}'       | 1
Run Code Online (Sandbox Code Playgroud)

一种可能性似乎是这样的:

select id, user_ids_who_like, (user_ids_who_like & '{514, 515}'::int[]) as jt  
from queryables 
where user_ids_who_like && '{514, 515}' 
order by icount(user_ids_who_like & '{514, 515}'::int[]) desc;
Run Code Online (Sandbox Code Playgroud)

但我不知道这种风格(使用 intarray 扩展而不是本机数组函数和运算符)是否已经过时;这里有更老练的用户的任何反馈吗?我不清楚如何使用methods 和 operators做两个数组的交集。

Erw*_*ter 6

仅使用基本 Postgres 安装工具,您可能会unnest()计算LATERAL子查询:

SELECT i.name, i.user_ids_who_like, x.ct
FROM   items i
     , LATERAL (
   SELECT count(*) AS ct
   FROM   unnest(i.user_ids_who_like) uid
   WHERE  uid = ANY('{3,4,11}'::int[])
   ) x
ORDER  BY x.ct DESC;  -- add PK as tiebreaker for stable sort order
Run Code Online (Sandbox Code Playgroud)

我们不需要 aLEFT JOIN来保留没有匹配的行,因为count()总是返回一行 - 0 表示“不匹配”。

intarray

假设integer阵列而不NULL值或重复时,交叉点算子&的的intarray模块将是更简单的:

SELECT name, user_ids_who_like
     , array_length(user_ids_who_like & '{3,4,11}', 1) AS ct
FROM   items
ORDER  BY 3 DESC NULLS LAST;
Run Code Online (Sandbox Code Playgroud)

NULLS LAST最后添加了对空数组进行排序 - 在您稍后的问题提醒之后:

intarray为此,每个数据库安装一次。

在子句中使用重叠运算符&&WHERE排除没有任何重叠的行:

SELECT ...
FROM   ...
WHERE user_ids_who_like && '{3,4,11}'
ORDER  BY ...
Run Code Online (Sandbox Code Playgroud)

为什么?根据文档:

intarray提供了用于索引支持&&@><@,和@@运营商,以及规则阵列平等。

以类似的方式应用于标准数组运算符。细节:


或者更激进的是,使用单独的表而不是数组列的规范化模式user_ids_who_like会占用更多的磁盘空间,但为这些问题提供了带有纯 btree 索引的简单解决方案。