Справка по SQL с запросом MAX

У меня есть таблица стран с именем bbc (имя, регион, площадь, население, ВВП)

Мне нужна таблица с регионом, названием и населением крупнейших (самых густонаселенных) стран по регионам. До сих пор я пробовал это:

SELECT region, name, MAX(population)
FROM bbc
GROUP BY region

Это дало мне сообщение об ошибке: ORA-00979: Not a GROUP BY Expression

Я попытался изменить на GROUP BY регион, имя, но это не дает мне правильную таблицу


person tiagovrtr    schedule 09.07.2011    source источник
comment
Что происходит со вторым MAX(population)? Где ты это видел?   -  person mechanical_meat    schedule 09.07.2011
comment
Хорошо, но не позволяйте этому повториться :p   -  person mechanical_meat    schedule 10.07.2011


Ответы (3)


Вот самый простой и короткий способ сделать это, поскольку в Oracle есть тестирование кортежей, это может сделать код короче:

Во-первых, получите максимальное население в каждом регионе:

SELECT region, MAX(population)
FROM bbc
GROUP BY region

Затем проверьте страны против него:

select region, name, population 
from bbc 
where (region, population) in
      (SELECT region, MAX(population)
       FROM bbc
       GROUP BY region)
order by region

Если вы хотите поддерживать множество СУБД, используйте EXISTS:

select region, name, population 
from bbc o
where exists
      (SELECT null -- neutral. doesn't invoke Cargo Cult Programming ;-)
       FROM bbc
       WHERE region = o.region 
       GROUP BY region
       HAVING o.population = MAX(population) )
order by region

Запрос проверен здесь, оба имеют одинаковый результат: http://sqlzoo.net/0.htm

http://www.ienablemuch.com/2010/05/why-is-exists-select-1-cargo-cult.html

person Michael Buen    schedule 10.07.2011
comment
Оба этих запроса имеют более высокую стоимость (Explain Plan), чем запрос с использованием аналитики. Все зависит от ваших целей. - person DCookie; 10.07.2011
comment
Это доказательство явным утверждением ;-) что мы делаем перед анализом? Я могу рискнуть предположить, что в этом подходе больше компьютерных наук, чем аналитика, что функциональность существует задолго до того, как на сцену вышли эти CTE/Windowing/Analytics. С точки зрения реализации C/C++, аналитика содержит много if внутри, она обрабатывает множество функций, поэтому может ли она быть медленнее? может быть. Просто мои 2 цента. Ответ в профиле - person Michael Buen; 10.07.2011
comment
Ни в коем случае не доказательство - просто наблюдение того, сколько, по мнению оптимизатора, будет стоить выполнение запроса. Вы абсолютно правы, что доказательство находится в метриках. - person DCookie; 10.07.2011

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

SELECT name, region, population
  FROM (SELECT region, name, population
             , MAX(population) OVER (PARTITION BY region) maxpop
          FROM bbc)
 WHERE population = maxpop;

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

Чтобы проиллюстрировать с ограниченным примером:

SELECT * FROM bbc;

REGION          NAME        POPULATION
--------------- -------     ----------
North America   USA         300000000
North America   Canada      100000000
North America   Mexico       50000000
South America   Brazil       50000000
South America   Argentina    40000000
South America   Venezuela    20000000

Добавьте аналитическую функцию:

SELECT region, NAME, population
     , MAX(population) OVER (PARTITION BY region) maxpop
  FROM bbc;

REGION          NAME                POPULATION      MAXPOP
--------------- -------             ----------      ----------
North America   USA                 300000000       300000000
North America   Canada              100000000       300000000
North America   Mexico               50000000       300000000
South America   Brazil               50000000        50000000
South America   Argentina            40000000        50000000
South America   Venezuela            20000000        50000000

Затем готовый продукт:

NAME    REGION             POPULATION
------- ---------------    -----------
USA     North America       300000000
Brazil  South America        50000000

Еще одно редактирование. Вы можете избежать выбора гнезда, но не подзапроса:

SELECT NAME, region, population
  FROM bbc
 WHERE (region, population) IN
       (SELECT region, MAX(population)
          FROM bbc
         group by region);
person DCookie    schedule 09.07.2011
comment
Для данного региона, если две страны имеют одинаковое население (что, я думаю, крайне маловероятно, но всегда стоит спросить), этот запрос вернет обе: это то, чего ждет тиаговртр? - person Bruno Gautier; 10.07.2011
comment
Кто знает? Не в требованиях! - person DCookie; 10.07.2011
comment
@BrunoGautier, конечно, работает, но я пытался не использовать вложенные выборки. Это невозможно? - person tiagovrtr; 10.07.2011
comment
@OMG, всегда есть несколько способов сделать это ;-) Интересно, есть ли разница в производительности.... - person DCookie; 10.07.2011
comment
@tiagovtr, не знаю, почему вы хотите избежать вложенных выборов, но мое редактирование показывает, как это сделать с помощью подзапроса. - person DCookie; 10.07.2011
comment
@tiagowrtr, почему не вложенные выборки? Я думаю, что запросы с вложенными выборками обычно более читаемы, потому что вы можете легче увидеть шаги, необходимые для получения конечного результата, особенно при использовании аналитики. А еще мне легче угадать план выполнения. - person Bruno Gautier; 10.07.2011
comment
@OMG, я действительно думал об использовании функции ‹code›ROW_NUMBER‹/code› ;-) - person Bruno Gautier; 10.07.2011
comment
@DCookie, мне нравится твоя иллюстрация, она очень хорошо показывает причины использования аналитики. - person Bruno Gautier; 10.07.2011

В подавляющем большинстве ваз ошибка ORA-00979 вызвана тем, что неагрегированный столбец не включен в предложение GROUP BY. В этом случае вам также необходимо включить имя в предложение GROUP BY. Кроме того, вы не должны вызывать функцию MAX в своем операторе FROM.

SELECT region, name, MAX(population)
FROM bbc
GROUP BY region, name
person THE DOCTOR    schedule 09.07.2011