【问题标题】:Can I do a max(count(*)) in SQL?我可以在 SQL 中执行 max(count(*)) 吗?
【发布时间】:2011-01-27 01:36:30
【问题描述】:

这是我的代码:

select yr,count(*)
from movie
join casting on casting.movieid=movie.id
join actor on casting.actorid = actor.id
where actor.name = 'John Travolta'
group by yr;

问题来了:

对于“约翰·特拉沃尔塔”来说,哪几年是最忙碌的。显示他每年制作的电影数量。

这是表结构:

movie(id, title, yr, score, votes, director)
actor(id, name)
casting(movieid, actorid, ord)

这是我得到的输出:

yr      count(*)
1976    1
1977    1
1978    1
1981    1
1994    1
-- etc.

我需要获取count(*) 最大的行。 我该怎么做?

【问题讨论】:

  • 该问题未能披露 RDBMS 和版本。答案很大程度上取决于它。
  • 最聪明的(并且可能最快的答案没有子查询)是here:使用LIMIT 1 来避免子查询。

标签: sql sql-order-by greatest-n-per-group window-functions


【解决方案1】:

您可以将topwith ties 一起使用,这将包括具有最大count(*) 值的所有年份,如下所示:

select top (1) with ties yr, count(*)
from movie
   join casting 
      on casting.movieid=movie.id
   join actor 
      on casting.actorid = actor.id
where actor.name = 'John Travolta'
group by yr;
order by count(*) desc

如果最大值为 6,您将获得计数值为 6 的所有年份。

【讨论】:

    【解决方案2】:

    这个问题很老,但是是referenced in a new question on dba.SE。我觉得没有提供最好的解决方案。此外,还有更快的新选项。

    标题中的问题

    我可以在 SQL 中执行max(count(*)) 吗?

    是的,您可以通过在 window function 中嵌套聚合函数来实现:

    SELECT m.yr, count(*) AS movie_count
         , max(count(*)) OVER () AS max_ct
    FROM   casting c
    JOIN   movie   m ON c.movieid = m.id
    WHERE  c.actorid = (SELECT id FROM actor WHERE name = 'John Travolta')
    GROUP  BY m.yr
    ORDER  BY count(*) DESC;
    

    db小提琴here

    这是标准 SQL。 Postgres 在 8.4 版(2009-07-01 发布,在提出这个问题之前)引入了它。其他 RDBMS 应该能够做到这一点。 考虑SELECT 查询中的事件顺序:

    可能的缺点:窗口函数不聚合行。在聚合步骤之后,您会得到 all 行。在某些查询中很有用,但不适合这个查询。

    要获得计数最高的一行,您可以使用ORDER BY ct LIMIT 1

    SELECT c.yr, count(*) AS ct
    FROM   actor   a
    JOIN   casting c ON c.actorid = a.id
    WHERE  a.name = 'John Travolta'
    GROUP  BY c.yr
    ORDER  BY ct DESC
    LIMIT  1;
    

    仅使用基本的 SQL 功能,在任何中度不错的 RDBMS 中都可用 - LIMIT 的实现各不相同:

    或者您可以每组一行使用DISTINCT ON(仅限 Postgres)获得最高计数:

    实际问题

    我需要获取count(*) 最大的行。

    计数最多的可能不止一行。

    SQL Server 拥有 WITH TIES 功能已有一段时间了 - 使用非标准语法:

    SELECT TOP 1 WITH TIES
           m.yr, count(*) AS movie_count
    FROM   casting c
    JOIN   movie   m ON c.movieid = m.id
    WHERE  c.actorid = (SELECT id FROM actor WHERE name = 'John Travolta')
    GROUP  BY m.yr
    ORDER  BY count(*) DESC;  -- can't sort by year for this
    

    db小提琴here

    PostgreSQL 13 使用标准 SQL 语法添加了 WITH TIES

    SELECT m.yr, count(*) AS movie_count
    FROM   casting c
    JOIN   movie   m ON c.movieid = m.id
    WHERE  c.actorid = (SELECT id FROM actor WHERE name = 'John Travolta')
    GROUP  BY m.yr
    ORDER  BY count(*) DESC  -- can't sort by year for this
    FETCH  FIRST 1 ROWS WITH TIES;
    

    db小提琴here

    这应该是最快的查询。延伸阅读:

    要按附加条件对结果进行排序(或者对于旧版本的 Postgres 或其他没有 WITH TIES 的 RDBMS),请在子查询中使用窗口函数 rank()

    SELECT yr, movie_count
    FROM  (
       SELECT m.yr, count(*) AS movie_count
            , rank() OVER (ORDER BY count(*) DESC) AS rnk
       FROM   casting c
       JOIN   movie   m ON c.movieid = m.id
       WHERE  c.actorid = (SELECT id FROM actor WHERE name = 'John Travolta')
       GROUP  BY m.yr
       ) sub
    WHERE  rnk = 1
    ORDER  BY yr;  -- optionally sort by year
    

    现在所有主要的 RDBMS 都支持窗口函数。

    【讨论】:

    • [...您根本不需要加入电影表]。需要连接到表“电影”,因为这是唯一具有列“年”(电影年)的表。
    • @KevinSwann:我以某种方式错过了这个,抱歉。你是对的,我相应地修复了。在使用过程中添加了新的、更好的选择。
    【解决方案3】:

    感谢最后的回答

    SELECT yr, COUNT(title)
    FROM actor
    JOIN casting ON actor.id = casting.actorid
    JOIN movie ON casting.movieid = movie.id
    WHERE name = 'John Travolta'
    GROUP BY yr HAVING COUNT(title) >= ALL
      (SELECT COUNT(title)
       FROM actor
       JOIN casting ON actor.id = casting.actorid
       JOIN movie ON casting.movieid = movie.id
       WHERE name = 'John Travolta'
       GROUP BY yr)
    

    我遇到了同样的问题:我只需要知道其计数与最大计数匹配的记录(可能是一条或多条记录)。

    我必须更多地了解“ALL 子句”,而这正是我正在寻找的那种简单的解决方案。

    【讨论】:

      【解决方案4】:
      create view sal as
      select yr,count(*) as ct from
      (select title,yr from movie m, actor a, casting c
      where a.name='JOHN'
      and a.id=c.actorid
      and c.movieid=m.id)group by yr
      

      -----查看已创建-----

      select yr from sal
      where ct =(select max(ct) from sal)
      

      年 2013

      【讨论】:

        【解决方案5】:

        来自这个网站 - http://sqlzoo.net/3.htm 2 种可能的解决方案:

        前 1 名是 ORDER BY ... DESC:

        SELECT yr, COUNT(title) 
        FROM actor 
        JOIN casting ON actor.id=actorid
        JOIN movie ON movie.id=movieid
        WHERE name = 'John Travolta'
        GROUP BY yr
        HAVING count(title)=(SELECT TOP 1 COUNT(title) 
        FROM casting 
        JOIN movie ON movieid=movie.id 
        JOIN actor ON actor.id=actorid
        WHERE name='John Travolta'
        GROUP BY yr
        ORDER BY count(title) desc)
        

        使用 MAX:

        SELECT yr, COUNT(title) 
        FROM actor  
        JOIN casting ON actor.id=actorid    
        JOIN movie ON movie.id=movieid
        WHERE name = 'John Travolta'
        GROUP BY yr
        HAVING 
            count(title)=
                (SELECT MAX(A.CNT) 
                    FROM (SELECT COUNT(title) AS CNT FROM actor 
                        JOIN casting ON actor.id=actorid
                        JOIN movie ON movie.id=movieid
                            WHERE name = 'John Travolta'
                            GROUP BY (yr)) AS A)
        

        【讨论】:

          【解决方案6】:
          SELECT * from 
          (
          SELECT yr as YEAR, COUNT(title) as TCOUNT
          FROM actor
          JOIN casting ON actor.id = casting.actorid
          JOIN movie ON casting.movieid = movie.id
          WHERE name = 'John Travolta'
          GROUP BY yr
          order by TCOUNT desc
          ) res
          where rownum < 2
          

          【讨论】:

          • ORDER BY without LIMIT/TOP 在子查询中无效。
          【解决方案7】:

          下面的代码给你答案。它本质上是通过使用 ALL 来实现 MAX(COUNT(*))。它的优点是它使用非常基本的命令和操作。

          SELECT yr, COUNT(title)
          FROM actor
          JOIN casting ON actor.id = casting.actorid
          JOIN movie ON casting.movieid = movie.id
          WHERE name = 'John Travolta'
          GROUP BY yr HAVING COUNT(title) >= ALL
            (SELECT COUNT(title)
             FROM actor
             JOIN casting ON actor.id = casting.actorid
             JOIN movie ON casting.movieid = movie.id
             WHERE name = 'John Travolta'
             GROUP BY yr)
          

          【讨论】:

            【解决方案8】:

            使用带有限制的 max 只会给您第一行,但如果有两行或多行具有相同数量的最大电影,那么您将错过一些数据。如果您有 rank() 函数可用,以下是一种方法。

            SELECT
                total_final.yr,
                total_final.num_movies
                FROM
                ( SELECT 
                    total.yr, 
                    total.num_movies, 
                    RANK() OVER (ORDER BY num_movies desc) rnk
                    FROM (
                           SELECT 
                                  m.yr, 
                                  COUNT(*) AS num_movies
                           FROM MOVIE m
                           JOIN CASTING c ON c.movieid = m.id
                           JOIN ACTOR a ON a.id = c.actorid
                           WHERE a.name = 'John Travolta'
                           GROUP BY m.yr
                         ) AS total
                ) AS total_final 
               WHERE rnk = 1
            

            【讨论】:

              【解决方案9】:

              用途:

                SELECT m.yr, 
                       COUNT(*) AS num_movies
                  FROM MOVIE m
                  JOIN CASTING c ON c.movieid = m.id
                  JOIN ACTOR a ON a.id = c.actorid
                              AND a.name = 'John Travolta'
              GROUP BY m.yr
              ORDER BY num_movies DESC, m.yr DESC
              

              num_movies DESC 排序会将最高值放在结果集的顶部。如果多个年份具有相同的计数,m.yr 会将最近的年份放在顶部...直到下一个 num_movies 值发生变化。

              我可以使用 MAX(COUNT(*)) 吗?


              不,您不能在同一个 SELECT 子句中将聚合函数层叠在一起。内部聚合必须在子查询中执行。即:

              SELECT MAX(y.num)
                FROM (SELECT COUNT(*) AS num
                        FROM TABLE x) y
              

              【讨论】:

              • 是的,您可以使用 MAX(COUNT(*)),但在 Oracle 中。 techonthenet.com/sql/max.php
              • @OMG Ponies - 这个答案非常好 - 您提供的第一个 SQL 确实有效(obvs)但是您提供的第二个 SQL 非常优雅,并且使我也能够更好地理解子查询!感谢您不厌其烦地给出完全扩展的答案。我试图做到这一点 - 但也有一个 Group By 在那里 - 这样做很可能!
              • 只是为了进一步理解这一点-您将如何使用第二个查询来获取电影数量最多的年份?就像现在一样,它可以确定在电影最多的一年中发生了多少部电影 - 但它不会给出那是哪一年。我很想了解如何从子查询中传回第二个值,这是与 MAX 结果相关的 Group By 值
              【解决方案10】:
                   select top 1 yr,count(*)  from movie
              join casting on casting.movieid=movie.id
              join actor on casting.actorid = actor.id
              where actor.name = 'John Travolta'
              group by yr order by 2 desc
              

              【讨论】:

                【解决方案11】:

                取决于您使用的数据库...

                select yr, count(*) num from ...
                order by num desc
                

                我的大部分经验是在 Sybase 中,它使用一些与其他 DB 不同的语法。但在这种情况下,您将命名您的计数列,因此您可以按降序对其进行排序。您可以更进一步,将结果限制在前 10 行(找出他最忙的 10 年)。

                【讨论】:

                  【解决方案12】:

                  只需通过count(*) desc 订购,您将获得最高的(如果您将其与limit 1 结合使用)

                  【讨论】:

                  • 现在,如果我有几行具有最大值并想要显示所有具有“最大值”的值
                  • @WhyCry:不完全确定你想问什么,但如果你在 Stackoverflow 上找不到答案,你应该把它作为一个单独的问题问:)
                  • @Wolph 他说你可以有多个具有相同最大值的行,限制为 1 你看不到
                  • 重读那条(3 岁)评论后,我认为他正在寻找HAVING MAX(...) = ...
                  猜你喜欢
                  • 2018-07-24
                  • 2011-05-13
                  • 2012-10-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-04-02
                  • 2021-09-11
                  相关资源
                  最近更新 更多