【问题标题】:Intersection of Records in PostgresPostgres 中的记录交集
【发布时间】:2020-11-14 19:02:12
【问题描述】:

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

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?意思是只返回上面示例中的 store_2,因为 store_2 与 label_1、label_2 和 label_3 相关联?我想要一个通用的方法来处理我有 n 个标签的情况。

【问题讨论】:

  • “因为 store_2 与 label_1、label_2 和 label_3 相关联”。我迷路了。 store_1 具有该关联,而不是 store_2
  • @GordonLinoff:我在寻找 label_1_stores = [store_1, store_2, store_3], label_2_stores = [store_2, store_3] 和 label_3_stores = [store_1, store_2] 的交集。这 3 个数组的交集只有 store_2。

标签: sql database postgresql jooq relational-division


【解决方案1】:

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

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)

【讨论】:

  • 感谢您的快速回复。如果我想提供标签输入并找到与这些标签关联的所有商店的交集怎么办?有没有什么好的方法可以修改你现在已有的 SQL?
【解决方案2】:

由于您也在寻找 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

简单的英语:

给我所有商店(股息),没有标签(t2),该商店(股息)没有条目(t1)

或者换句话说

如果有一个标签 (t2) 商店(股息)没有 (t1),那么该商店(股息)将没有所有可用的标签。

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

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

【讨论】:

    【解决方案3】:

    然后将@GMB 的查询转换为一个SQL 函数,该函数接受一个数组并返回一个store_id 的表。

    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);
    $$;
    

    那么所需要的只是一个简单的选择。查看完整示例here

    【讨论】:

    • 太棒了!看来这正是我所需要的!
    【解决方案4】:

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

    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(*);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-23
      • 2021-05-13
      • 2018-04-16
      • 1970-01-01
      • 2020-10-11
      相关资源
      最近更新 更多