Должен появиться в предложении GROUP BY или использоваться в агрегатной функции

У меня есть таблица, которая выглядит как этот вызывающий 'makerar'

 cname  | wmname |          avg           
--------+-------------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

И я хочу выбрать максимальное avg для каждого cname.

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

но я получу ошибку,

ERROR:  column "makerar.wmname" must appear in the GROUP BY clause or be used in an   aggregate function 
LINE 1: SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname;

поэтому я делаю это

SELECT cname, wmname, MAX(avg)  FROM makerar GROUP BY cname, wmname;

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

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  | 1.00000000000000000000
 spain  | usopp  |     5.0000000000000000

Фактические результаты должны быть

 cname  | wmname |          max           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

Как я могу решить эту проблему?

Примечание. Эта таблица представляет собой ПРОСМОТР, созданный из предыдущей операции.

114
26 окт. '13 в 4:50
источник поделиться
6 ответов

Да, это общая проблема агрегации. Перед SQL3 (1999) выбранные поля должны появиться в предложении GROUP BY [*].

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

SELECT m.cname, m.wmname, t.mx
FROM (
    SELECT cname, MAX(avg) AS mx
    FROM makerar
    GROUP BY cname
    ) t JOIN makerar m ON m.cname = t.cname AND t.mx = m.avg
;

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

Но вы также можете использовать функции окна, которые выглядят проще:

SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;

Единственное, что с этим методом состоит в том, что он отобразит все записи (функции окна не группируются). Но он покажет правильную (то есть максимальную на уровне cname) MAX для страны в каждой строке, так что это зависит от вас:

 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | luffy  |     5.0000000000000000
 spain  | usopp  |     5.0000000000000000

Решение, возможно менее изящное, чтобы показать только те кортежи (cname, wmname), соответствующие максимальному значению, это:

SELECT DISTINCT /* distinct here matters, because maybe there are various tuples for the same max value */
    m.cname, m.wmname, t.avg AS mx
FROM (
    SELECT cname, wmname, avg, ROW_NUMBER() OVER (PARTITION BY avg DESC) AS rn 
    FROM makerar
) t JOIN makerar m ON m.cname = t.cname AND m.wmname = t.wmname AND t.rn = 1
;


 cname  | wmname |          mx           
--------+--------+------------------------
 canada | zoro   |     2.0000000000000000
 spain  | usopp  |     5.0000000000000000

[*]: Интересно, что, несмотря на то, что spec-тип позволяет выбирать негрупповые поля, основным двигателям, похоже, это не очень нравится. Oracle и SQLServer просто не позволяют этого вообще. Mysql используется для его разрешения по умолчанию, но теперь, начиная с 5.7, администратор должен включить эту опцию (ONLY_FULL_GROUP_BY) вручную в конфигурации сервера, чтобы эта функция поддерживалась...

98
26 окт. '13 в 5:02
источник

Связанные вопросы


Похожие вопросы

В Postgres вы также можете использовать специальный синтаксис DISTINCT ON (expression):

SELECT DISTINCT ON (cname) 
    cname, wmname, avg
FROM 
    makerar 
ORDER BY 
    cname, avg DESC ;
78
01 нояб. '13 в 12:05
источник
SELECT t1.cname, t1.wmname, t2.max
FROM makerar t1 JOIN (
    SELECT cname, MAX(avg) max
    FROM makerar
    GROUP BY cname ) t2
ON t1.cname = t2.cname AND t1.avg = t2.max;

Использование rank() функция окна:

SELECT cname, wmname, avg
FROM (
    SELECT cname, wmname, avg, rank() 
    OVER (PARTITION BY cname ORDER BY avg DESC)
    FROM makerar) t
WHERE rank = 1;

Примечание

Любой из них сохранит несколько максимальных значений для каждой группы. Если вам нужна только одна запись на группу, даже если есть более одной записи с avg равным max, вы должны проверить ответ @ypercube.

9
26 окт. '13 в 5:06
источник

Проблема с указанием негруппованных и неагрегатных полей в group by выбирает, является ли этот движок не способ узнать, какое поле записи он должен вернуть в этом случае. Это первое? Это последнее? Обычно нет записи, которая, естественно, соответствует агрегированному результату (min и max являются исключениями).

Тем не менее, существует обходное решение: также необходимо заполнить требуемое поле. В posgres это должно работать:

SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;

Обратите внимание, что это создает массив всех wnames, упорядоченных по avg, и возвращает первый элемент (массивы в postgres основаны на 1).

7
20 нояб. '15 в 18:51
источник

Недавно я столкнулся с этой проблемой при попытке подсчета с помощью case when и обнаружил, что изменение порядка операторов which и count устраняет проблему:

SELECT date(dateday) as pick_day,
COUNT(CASE WHEN (apples = 'TRUE' OR oranges 'TRUE') THEN fruit END)  AS fruit_counter

FROM pickings

GROUP BY 1

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

CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter
0
09 нояб. '16 в 17:07
источник

Это тоже работает

SELECT *
FROM makerar m1
WHERE m1.avg = (SELECT MAX(avg)
                FROM makerar m2
                WHERE m1.cname = m2.cname
               )
0
31 янв. '17 в 0:52
источник

Посмотрите другие вопросы по меткам или Задайте вопрос