查找连接到所有给定标签的电影
Posted
技术标签:
【中文标题】查找连接到所有给定标签的电影【英文标题】:Find movies connected to all given tags 【发布时间】:2021-10-08 15:55:36 【问题描述】:我不知道如何表达我的 SQL 问题,所以我将输入一个预期的场景。
MovieID | TagID |
---|---|
1 | 1 |
1 | 3 |
1 | 5 |
2 | 1 |
2 | 2 |
2 | 3 |
3 | 1 |
3 | 3 |
3 | 5 |
3 | 7 |
我想选择至少与所有这些标签[1,3,5]
有标签关系的movieId。
所以预期的输出是1,3
。
【问题讨论】:
【参考方案1】:您可以将 tagid 聚合成一个数组并使用 contains 运算符:
select movieid
from the_table
where tagid in (1,3,5)
group by movieid
having array_agg(tagid) @> array[1,3,5]
【讨论】:
where
子句在逻辑上是多余的。也许对提高速度有好处。
@Stefanov.sm:从逻辑上讲,是的。但它提高了性能,因为它避免了聚合所有行。
我只是在编辑我的评论这样说:)
@Stefanov.sm 我需要能够找到作为 @> 数组子句的 where 标签和交集的并集的标签【参考方案2】:
relational-division的经典案例。
这是一个查询技术库:
How to filter SQL results in a has-many-through relation最佳解决方案取决于完整的情况。 Postgres 版本、表大小、基数、索引、性能要求、结果列……
通常是最快的选项之一:
SELECT movie_id
FROM tbl t1
JOIN tbl t2 USING (movie_id)
JOIN tbl t3 USING (movie_id)
WHERE t1.tag_id = 1
AND t2.tag_id = 3
AND t3.tag_id = 5;
需要(tag_id)
上的索引,或者更好的是(tag_id, movie_id)
上的快速索引。
如果tbl
实现了多对多关系 - 请参阅:
那么这两列应该有PRIMARY KEY
或UNIQUE
约束。手头的查询更喜欢(tag_id, movie_id)
而不是(movie_id, tag_id)
。见:
如果列统计信息是最新的(请参阅autovacuum
、ANALYZE
),Postgres 很可能会从最有选择性的标签开始,以尽早消除不合格的电影。
【讨论】:
感谢您的链接。我无法用谷歌搜索我的问题,因为我什至不知道查询的主题是什么 在最快的选项中 我非常怀疑是否可以将多个自连接视为快速选项。其他两种解决方案中的任何一种都会表现得更好。此外,这对于超过 3 个值的扩展非常可怕。 @forpas:运行一些测试(就像我做过很多次一样)并随时纠正您的错误声明。也许其中一些适用于您更习惯的其他 RDBMS。【参考方案3】:我确定 a_horse_with_no_name 的答案有效,而且非常干净。
这是一种使用更多普通 sql 的方法。
WITH vals (search_value) AS (VALUES (1),(3),(5))
SELECT id
FROM (
SELECT DISTINCT
mv.id,
mv.TagID
FROM movie mv
INNER
JOIN vals v
ON mv.tagID = v.search_value
)
GROUP
BY id
HAVING COUNT(1) = (SELECT COUNT(*) FROM vals)
编辑:只是说出来,因为这种类型的问题经常出现在我面前,而不是有一个定义的值列表,我对另一个表中的列中的一组值感兴趣。这种方法可以在这种情况下使用,而无需进行任何硬编码。
【讨论】:
好吧,@a_horse_with_no_name 的解决方案可以修改和重用,而无需硬编码任何东西。数组字面量将替换为简单的数组值标量子查询。 @Stefanov.sm 好点,说实话,我不是 Postgres 人,也不知道这些功能。【参考方案4】:通过对来自@a_horse_with_no_name 的查询稍作修改,您可以避免重复输入目标标签。使用 CTE 定义数组,然后 unnest
形成 in
子句。 (见demo)
with tag_list (tags) as
(select array [tag1,tag2,tag3,...])
select distinct on (mt.movie_id) movie_id::integer
from <table_name> mt
where mt.tag_id in ( select unnest(tags) from tag_list)
group by mt.movie_id
having array_agg(tag_id) @> (select tags from tag_list);
如果经常运行,您可以将其包装在 SQL 函数中,从而构建参数化查询:
create or replace
function get_movies_with_all_tags( tags_in integer[])
returns table (movie_id integer)
language sql
as $$
select mt.movie_id
from <table_name> mt
where mt.tag_id in ( select unnest(tags_in))
group by mt.movie_id
having array_agg(tag_id) @> tags_in
order by mt.movie_id;
$$;
【讨论】:
以上是关于查找连接到所有给定标签的电影的主要内容,如果未能解决你的问题,请参考以下文章
如何使用opencv或其他c ++库查找图像中(邻接)标签之间的连接关系?