【问题标题】:How to limit the max counts如何限制最大计数
【发布时间】:2018-06-02 00:32:14
【问题描述】:

我想为每个类型的电影找到N个在大多数类型的电影中出演过的演员

Tables and their columns:

actor(actor_id,name)
role(actor_id,movie_id)
movie(movie_id,title)
movie_has_genre(movie_id,genre_id)
genre(genre_id,genre_name)

通过这个查询,我可以找到在同类型电影中出演最多的演员。

select t1.genre_name, t1.actor_id, t1.max_value
from
(
    select g.genre_name, a.actor_id, count(*) as max_value
    from genre g
    inner join movie_has_genre mhg on mhg.genre_id = g.genre_id
    inner join movie m on mhg.movie_id = m.movie_id
    inner join role r on m.movie_id = r.movie_id
    inner join actor a on a.actor_id = r.actor_id
    group by g.genre_name, a.actor_id
) t1
inner join
(
    select genre_name, MAX(max_value) AS max_value
    from
    (
        select g.genre_name, a.actor_id, count(*) as max_value
        from genre g
        inner join movie_has_genre mhg on mhg.genre_id = g.genre_id
        inner join movie m on mhg.movie_id = m.movie_id
        inner join role r on m.movie_id = r.movie_id
        inner join actor a on a.actor_id = r.actor_id
        group by g.genre_name, a.actor_id
    ) t
    GROUP BY genre_name
) t2
    ON t1.genre_name = t2.genre_name and t1.max_value = t2.max_value
ORDER BY
    t1.max_value desc;

但我想将演员的数量限制为 1。那我该怎么做呢?

例子:

我得到的结果:

genre_name | actor_id | max_value
==================================
Thriller   |  22591   |   7
Drama      |  22591   |   6
Crime      |  65536   |   3
Horror     |  22591   |   3
Action     |  292028  |   3
Action     |  378578  |   3
Action     |  388698  |   3

我想要的结果:

 genre_name | actor_id | max_value
==================================
Thriller    | 22591    | 7
Drama       | 22591    | 6
Crime       | 65536    | 3
Horror      | 22591    | 3
Action      | 292028   | 3

【问题讨论】:

  • 如果您有平局,您需要定义选择 1 个参与者的逻辑。这是一个业务逻辑决策,而不是编程决策。一旦你下定决心,实施起来就会容易得多。
  • 好吧,假设我想通过 actor_id 作为最小值来限制它。(几乎我选择了 933 actor_id 的人而不是 2591 actor_id 的人)。我的问题是我应该在哪里实现它和为什么我搜索了几个小时却找不到东西。
  • 这个问题根本没有反对意见,所以不知道你在说什么。我也没有否决您之前提出的另一个问题。只是将其关闭为重复项。两者不一样。

标签: mysql


【解决方案1】:

如果您只想随机选择一个演员,只需将以下行添加到您的代码中:

 select genre_name, actor_id, max_value
 from
 (
     select g.genre_name, a.actor_id, count(*) as max_value
     from genre g
     inner join movie_has_genre mhg on mhg.genre_id = g.genre_id
     inner join movie m on mhg.movie_id = m.movie_id
     inner join role r on m.movie_id = r.movie_id
     inner join actor a on a.actor_id = r.actor_id
     group by g.genre_name, a.actor_id
 ) t1
 inner join
 (
     select genre_name, MAX(max_value) AS max_value
     from
     (
         select g.genre_name, a.actor_id, count(*) as max_value
         from genre g
         inner join movie_has_genre mhg on mhg.genre_id = g.genre_id
         inner join movie m on mhg.movie_id = m.movie_id
         inner join role r on m.movie_id = r.movie_id
         inner join actor a on a.actor_id = r.actor_id
         group by g.genre_name, a.actor_id
     ) t
     GROUP BY genre_name
 ) t2
     USING(genre_name,max_value)
 GROUP BY genre_name, max_value
 ORDER BY max_value desc;

【讨论】:

  • 在 order by 上面试过了,我的 group by 出错了
  • 错误代码:1055。SELECT 列表的表达式 #2 不在 GROUP BY 子句中,并且包含在功能上不依赖于 GROUP BY 子句中的列的非聚合列 't1.actor_id';这与 sql_mode=only_full_group_by 0.031 秒不兼容
  • 我也改成这个了
  • 我也遇到了同样的问题
  • 错误代码:1055。SELECT 列表的表达式 #2 不在 GROUP BY 子句中,并且包含在功能上不依赖于 GROUP BY 子句中的列的非聚合列 't1.actor_id';这与 sql_mode=only_full_group_by 0.031 秒不兼容
【解决方案2】:

您使用的某些连接是多余的。

SELECT 
  U.genre_name, U.actor_id, U.actor_genre_count
FROM
    (SELECT 
      A.genre_id, A.genre_name, C.actor_id, count(*) actor_genre_count
    FROM genre A 
    JOIN movie_has_genre B
    ON A.genre_id=B.genre_id 
    JOIN role C 
    ON C.movie_id=B.movie_id
    GROUP BY A.genre_id, A.genre_name, C.actor_id) U
JOIN 
   (SELECT 
       S.genre_id, S.genre_name, MAX(S.actor_genre_count) max_actor_genre
    FROM
       (SELECT 
           A.genre_id, A.genre_name, C.actor_id, count(*) actor_genre_count
        FROM genre A
        JOIN movie_has_genre B
        ON A.genre_id=B.genre_id 
        JOIN role C 
        ON C.movie_id=B.movie_id
        GROUP BY A.genre_id, A.genre_name, C.actor_id) S
    GROUP BY S.genre_id, S.genre_name) V
ON U.genre_name=V.genre_name AND U.actor_genre_count=V.max_actor_genre;

【讨论】:

  • 我得到了与之前完全相同的结果。
【解决方案3】:

此解决方案改编自 this Stack Overflow answer 关于按名称限制结果。我试图做一个类似的查询,应该选择第一个 actor_id 并且只返回它。

SELECT id, CategoryName, image, date_listed, item_id
SELECT t1.genre_name, t1.actor_id, t1.actor_movie_count
FROM
    (
    SELECT g.genre_name, r.actor_id, COUNT(*) as actor_movie_count    
    FROM genre g
    INNER JOIN movie_has_genre mhg ON mhg.genre_id = g.genre_id
    INNER JOIN role r ON m.movie_id = r.movie_id
    GROUP BY g.genre_name, r.actor_id
    ) t1
LEFT JOIN
    (
    SELECT genre_name, actor_id, MAX(actor_movie_count) AS max_actor_movie_count
    FROM
         (
         SELECT g.genre_name, r.actor_id, COUNT(*) AS actor_movie_count
         FROM genre g
         INNER JOIN movie_has_genre mhg ON mhg.genre_id = g.genre_id
         INNER JOIN role r ON m.movie_id = r.movie_id
         GROUP BY g.genre_name, r.actor_id
         )
    GROUP BY genre_name
    ) t2
ON t1.genre_name = t2.genre_name AND t1.actor_movie_count = t2.max_actor_movie_count AND (t1.actor_id > t2.actor_id)
WHERE t2.genre_id IS NULL
ORDER BY t1.actor_movie_count DESC

如果这仍然不能解决您的问题,其他类似问题的解释如下:

所以answer about returning 1 row per group

所以question about limiting query answer to N results per group

所以question about selecting N items per category

外文:Finding the max/first of a particular group in SQL

【讨论】:

  • 我已经尝试过了,但我不能让它工作。而且不同的不起作用,因为我得到的每个结果都是独一无二的
【解决方案4】:

您可以使用相关的LIMIT 1 子查询来获取最常扮演该类型的演员的id

select g.genre_name, (
        select r.actor_id
        from movie_has_genre mg
        join role r on r.movie_id = mg.movie_id
        where mg.genre_id = g.genre_id
        group by r.actor_id
        order by count(*) desc,
                 r.actor_id asc -- on tie least actor_id wins
    ) as actor_id
from genre g

结果会是这样的:

genre_name | actor_id
======================
Thriller   | 22591   
Drama      | 22591   
Crime      | 65536   
Horror     | 22591   
Action     | 292028 

如您所见,计数不包括在内。如果您需要计数,简单的方法是将其返回到与actor_id 相同的字符串列中

将子查询中的SELECT子句改为

select concat(r.actor_id, ':', count(*)) as actor_id_count

这将在单个字符串列中返回 actor_id 和计数,例如

genre_name | actor_id_count
===========================
Thriller   |  22591:7

然后您可以在您的应用程序代码中解析它(使用splitexplode 或其他任何方式)。

具有CTE公用表表达式)和ROW_NUMBER()窗口函数)的解决方案(由MySQL 8 支持 和 MariaDB 10.2) 可以是:

with cte as (
    select g.genre_name, r.actor_id, count(*) as max_value,
       row_number() over (partition by g.genre_name order by count(*) desc, r.actor_id) as rn
    from genre g
    inner join movie_has_genre mhg on mhg.genre_id = g.genre_id
    inner join role r on mhg.movie_id = r.movie_id
    group by g.genre_name, r.actor_id
)
select genre_name, actor_id, max_value from cte where rn = 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-27
    • 2015-06-14
    • 2011-01-07
    • 1970-01-01
    • 1970-01-01
    • 2010-10-03
    相关资源
    最近更新 更多