Могу ли я сделать максимум (количество (*)) в SQL? - PullRequest
58 голосов
/ 13 марта 2010

Вот мой код:

    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
etcetc

Мне нужно получить строки, для которых count(*) макс.

Как мне это сделать?

Ответы [ 11 ]

67 голосов
/ 13 марта 2010

Использование:

  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.

Можно ли использовать МАКС (СЧЕТ (*))?


Нет, нельзя объединять агрегатные функции друг над другом в одном и том же предложении SELECT. Внутренний агрегат должен быть выполнен в подзапросе. IE:

SELECT MAX(y.num)
  FROM (SELECT COUNT(*) AS num
          FROM TABLE x) y
26 голосов
/ 13 марта 2010

Просто закажите по count(*) desc, и вы получите самый высокий (если вы объедините его с limit 1)

4 голосов
/ 21 июня 2012

это с этого сайта - http://sqlzoo.net/3.htm 2 возможных решения:

с ТОП 1 ЗАКАЗАТЬ ПО ... 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)
4 голосов
/ 29 июня 2011
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
3 голосов
/ 22 июня 2011

Следующий код дает вам ответ. По сути, он реализует MAX (COUNT (*)) с помощью ALL. Он имеет преимущество в том, что использует очень простые команды и операции.

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)
3 голосов
/ 13 марта 2010

Использование 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
2 голосов
/ 13 июня 2016

Этот вопрос старый, но на него ссылаются в новом вопросе на dba.SE . Я чувствую, что лучшие решения еще не были предоставлены, поэтому я добавляю еще одно.

Во-первых, при условии ссылочной целостности (обычно с ограничениями внешнего ключа) вам не нужно присоединяться к таблице movie вообще . Это мертвый груз в вашем запросе. Все ответы до сих пор не указывают на это.


Могу ли я сделать max(count(*)) в SQL?

Чтобы ответить на вопрос в заголовке: Да , в Postgres 8.4 (выпущен 2009-07-01, до того, как был задан этот вопрос) или позже, вы можете добиться этого, вложив агрегатную функцию в оконная функция :

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

Рассмотрим последовательность событий в запросе SELECT:

(Возможный) недостаток: оконные функции не агрегируют строки. Вы получите всех строк, оставшихся после этапа агрегирования. Полезно в некоторых запросах, но не идеально для этого.


Чтобы получить одну строку с наибольшим количеством, вы можете использовать ORDER BY ct LIMIT 1 как @ wolph hinted :

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, доступных в любой наполовину приличной СУБД - реализация LIMIT варьируется:

Или вы можете получить по одной строке на группу с наибольшим количеством DISTINCT ON (только Postgres):


Ответ

Но вы просили:

... строк, для которых количество (*) равно макс.

Возможно, больше, чем один. Наиболее элегантное решение - использование оконной функции rank() в подзапросе. Райан предоставил запрос , но это может быть проще (подробности в моем ответе выше):

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

В настоящее время все основные СУБД поддерживают оконные функции. За исключением MySQL и вилок ( MariaDB, кажется, реализовал их наконец в версии 10.2 ).

2 голосов
/ 13 марта 2010

В зависимости от того, какую базу данных вы используете ...

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

Большая часть моего опыта связана с Sybase, который использует некоторый синтаксис, отличный от других БД. Но в этом случае вы называете свой столбец count, чтобы вы могли отсортировать его в порядке убывания. Вы можете пойти еще дальше и ограничить свои результаты первыми 10 строками (чтобы найти его 10 самых занятых лет).

1 голос
/ 08 июня 2014

Благодаря последнему ответу

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», и это именно то простое решение, которое я искал.

1 голос
/ 13 марта 2010
     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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...