Postgres 中记录的交集

Intersection of Records in Postgres

假设我有多个关联商店的标签,如下所示:

label_id | store_id
--------------------
label_1  | store_1
label_1  | store_2
label_1  | store_3
label_2  | store_2
label_2  | store_3
label_3  | store_1
label_3  | store_2

在SQL(或jooq)中有什么好的方法可以获取标签交集的所有商店ID吗?在上面的示例中意味着 return store_2 因为 store_2 与 label_1、label_2 和 label_3 相关联?我想要一个通用的方法来处理我有 n 个标签的情况。

这是一个关系划分问题,您希望商店具有所有可能的标签。这是一种使用聚合的方法:

select store_id
from mytable
group by store_id
having count(*) = (select count(distinct label_id) from mytable)

请注意,这假定没有重复的 (store_id, label_id) 元组。否则,您需要将 having 子句更改为:

having count(distinct label_id) = (select count(distinct label_id) from mytable)

如果您想要三个特定的标签,您可以使用:

select store_id
from t
where label in (1, 2, 3)
group by store_id
having count(*) = 3;

如果您只想要这三个标签,那么:

select store_id
from t
group by store_id
having count(*) = 3 and
       count(*) filter (where label in (1, 2, 3)) = count(*);

然后将@GMB 的查询转换为一个 SQL 函数,该函数接受一个数组和 returns 一个 store_id 的 table。

create or replace 
function stores_with_all_labels( label_list text[] )
 returns table (store_id text) 
 language  sql
as $$
    select store_id
      from label_store
     where label_id = any (label_list)
     group by store_id
    having count(*) = array_length(label_list,1);
$$;

那么只需要一个简单的select。请参阅完整示例 here

由于您也在寻找 jOOQ 解决方案,jOOQ 支持 a synthetic relational division operator,它产生了一种更学术的关系除法方法,仅使用关系代数运算符:

// Using jOOQ
T t1 = T.as("t1");
T t2 = T.as("t2");

ctx.select()
   .from(t1.divideBy(t2).on(t1.LABEL_ID.eq(t2.LABEL_ID)).returning(t1.STORE_ID).as("t"))
   .fetch();

这会生成类似于以下查询的内容:

select t.store_id
from (
  select distinct dividend.store_id
  from t dividend
  where not exists (
    select 1
    from t t2
    where not exists (
      select 1
      from t t1
      where dividend.store_id = t1.store_id
      and t1.label_id = t2.label_id 
    )
  )
) t

用简单的英语:

Get me all the stores (dividend), for which there exists no label (t2) for which that store (dividend) has no entry (t1)

或者换句话说

If there was a label (t2) that a store (dividend) does not have (t1), then that store (dividend) would not have all the available labels.

这不一定比基于 GROUP BY / HAVING COUNT(*) 的关系划分实现(如其他答案所示)更具可读性或更快,事实上,GROUP BY / HAVING 基于解决方案在这里可能更可取,特别是因为只涉及一个 table。 jOOQ 的未来版本可能会使用 GROUP BY / HAVING 方法,而不是:#10450

但是在jOOQ中,这样写可能会很方便,求jOOQ解决方案:)