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解决方案:)
假设我有多个关联商店的标签,如下所示:
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解决方案:)