Как создать хороший запрос с "max (count (*))"? - PullRequest
0 голосов
/ 29 ноября 2018

Я должен сказать, кто является ученым, который был больше всего в миссии.Я попробовал этот код, но он не был успешным:

select name
from scientist, mission
where mission.nums = chercheur.nums
having count(*) = (select max(count(numis))  from mission, scientist where 
mission.nums = chercheur.nums
group by name)

Я сделал несколько модификаций для этого запроса, но я получаю только ошибки (ora-0095 и ora-0096, если я правильно помню).

Кроме того, я создаю свои таблицы с:

CREATE TABLE Scientist
(NUMS NUMBER(8),
NAME VARCHAR2 (15),
CONSTRAINT CP_CHER PRIMARY KEY (NUMS));

CREATE TABLE MISSION
(NUMIS NUMBER(8),
Country VARCHAR2 (15),
NUMS NUMBER(8),
CONSTRAINT CP_MIS PRIMARY KEY (NUMIS),
CONSTRAINT CE_MIS FOREIGN KEY (NUMS) REFERENCES SCIENTIST (NUMC));

Ответы [ 5 ]

0 голосов
/ 29 ноября 2018

Если вам нужна одна строка, то в Oracle 12+ вы можете сделать:

SELECT name, COUNT(*) AS cnt
FROM scientist s JOIN
     mission m
     ON s.nums = m.nums
GROUP BY name
ORDER BY COUNT(*) DESC
FETCH FIRST 1 ROW ONLY;

В более ранних версиях вы обычно используете подзапрос:

SELECT s.*
FROM (SELECT name, COUNT(*) AS cnt
      FROM scientist s JOIN
           mission m
           ON s.nums = m.nums
      GROUP BY name
      ORDER BY COUNT(*) DESC
     ) sm
WHERE rownum = 1;

Если выЕсли вы хотите связать, то, как правило, оконные функции будут простым решением:

SELECT s.*
FROM (SELECT name, COUNT(*) AS cnt,
             RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
      FROM scientist s JOIN
           mission m
           ON s.nums = m.nums
      GROUP BY name
      ORDER BY COUNT(*) DESC
     ) sm
WHERE seqnum = 1;
0 голосов
/ 29 ноября 2018

Я бы написал такой запрос:

SELECT NAME, COUNTER
FROM
(SELECT NAME, COUNT(*) AS COUNTER
FROM SCIENTIST S
LEFT JOIN MISSION M
ON S.NUMS=M.NUMS
GROUP BY NAME) NUM
INNER JOIN
(SELECT MAX(COUNTER) AS MAX_COUNTER FROM
(SELECT NAME, COUNT(*) AS COUNTER
FROM SCIENTIST S
LEFT JOIN MISSION M
ON S.NUMS=M.NUMS
GROUP BY NAME) C) MAX
ON NUM.COUNTER=MAX.MAX_COUNTER;

(работает на MYSQL, надеюсь, в Oracle тоже самое)

0 голосов
/ 29 ноября 2018

Вы можете сосчитать миссии, в которых участвовал каждый ученый, и заключить этот запрос в запрос с помощью оконной функции, которая будет ранжировать их в соответствии с их участием:

SELECT name
FROM   (SELECT name, RANK() OVER (PARTITION BY name ORDER BY cnt DESC) AS rk
        FROM   (SELECT   name, COUNT(*) AS cnt
                FROM     scientist s
                JOIN     mission m ON s.nums = m.nums
                GROUP BY name) t
       ) q
WHERE  rk = 1
0 голосов
/ 29 ноября 2018

Шаг 0: отформатируйте свой код :-) Было бы намного проще визуализировать

Шаг 1: Получите количество Numis по Nums в таблице Mission.Это скажет вам, сколько миссий было выполнено каждым Nums

Это делается в блоке cte cnt_by_nums

Далее, чтобы получить имя ученого, соединяя cnt_by_nums с таблицей ученого.

После этого вы хотите получить только тех ученых, которые имеют cnt_by_missions в качестве максимально доступного значения из cnt_by_num

with cnt_by_nums
  as (select Nums,count(Numis) as cnt_missions
          from mission
         group by Nums
      )
select a.Nums,max(b.Name) as name
  from cnt_by_nums a
  join scientist b
    on a.Nums=b.Nums 
group by a.Nums
having count(a.cnt_missions)=(select max(a1.cnt_missions) from cnt_by_nums a1)
0 голосов
/ 29 ноября 2018

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

select count(numis) from mission group by nums

Ваши имена столбцов немного странные, но это ваш выбор; -)

Можно выбрать только ученого с наибольшим количеством ссылок на миссиидвумя способами.Одним из подходов может быть ваш подход, когда вы можете получить несколько ученых, если у них одинаковые максимальные миссии.

Первая проблема, с которой вы столкнулись в своем запросе, заключается в том, что вы проверяете агрегацию (HAVING COUNT (*) =) безгруппировка.Вы только группируете свой подвыбор.

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

Подход только с одной строкой:

select s.name from scientist s, mission m 
where m.nums = s.nums 
group by name 
having count(*) = 
    (select count(numis) from mission 
    group by nums 
    order by 1 desc 
    fetch first 1 row only)

Подход с двойным подзапросом:

select s.name from scientist s, mission m 
where m.nums = s.nums 
group by name having count(*) = 
    (select max(numis) from 
        (select count(numis) numis from mission group by nums)
    )

Второй подход заключается в выполнении FETCH FIRST для вашего конечного результатано это даст вам ровно 1 ученого, даже если есть несколько с одинаковыми максимальными миссиями:

select s.name from scientist s, mission m 
where m.nums = s.nums 
group by name
order by count(*) desc
fetch first 1 row only

Выполнение картезианского продукта не является современным, но оптимизатор сделает его "хорошим соединением" суказанная ссылка в предложении where.

Сделано в IBM Db2, но также должно работать в Oracle.

...