查找连接到所有给定标签的电影

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 实现了多对多关系 - 请参阅:

How to implement a many-to-many relationship in PostgreSQL?

那么这两列应该有PRIMARY KEYUNIQUE 约束。手头的查询更喜欢(tag_id, movie_id) 而不是(movie_id, tag_id)。见:

Is a composite index also good for queries on the first field?

如果列统计信息是最新的(请参阅autovacuumANALYZE),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 ++库查找图像中(邻接)标签之间的连接关系?

从 iPhone 查找连接到本地网络的系统?

使用 db link 查找谁正在连接到数据库

给定数据库的 ODBC 连接字符串,如何修改我的 settings.py 以便我的 Django 应用程序连接到它?

查找连接到同一个 Wifi 网络的所有设备的 MAC 地址

如何使用给定的 JNDI 名称连接到 Websphere 数据源?