musi pojawić się w klauzuli GROUP BY lub być użyty w funkcji agregującej
Mam tabelę, która wygląda jak ten rozmówca 'makerar'
cname | wmname | avg
--------+-------------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
I chcę wybrać maksymalną wartość avg dla każdego cname.
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
Ale dostanę błąd,
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;
Więc robię to
SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname, wmname;
Jednak nie da to zamierzonych wyników, a niepoprawne wyniki poniżej są pokazane.
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 1.00000000000000000000
spain | usopp | 5.0000000000000000
Rzeczywiste wyniki powinny być
cname | wmname | max
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | usopp | 5.0000000000000000
Jak mogę rozwiązać ten problem?
Uwaga: Ta tabela jest widokiem utworzonym z poprzedniego operacja.
7 answers
Tak, jest to powszechny problem agregacji. Przed SQL3 (1999) , wybrane pola muszą pojawić się w GROUP BY
klauzuli[*].
Aby obejść ten problem, musisz obliczyć agregat w zapytaniu podrzędnym, a następnie połączyć go ze sobą, aby uzyskać dodatkowe kolumny, które musisz pokazać:
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
Ale można również korzystać z funkcji okien, które wyglądają prościej:
SELECT cname, wmname, MAX(avg) OVER (PARTITION BY cname) AS mx
FROM makerar
;
Jedyną rzeczą w tej metodzie jest to, że wyświetli ona wszystkie rekordy (funkcje okien nie Grupa). Ale pokaże poprawny (tj. maksymalnie na poziomie cname
) MAX
dla kraju w każdym wierszu, więc to zależy od ciebie:
cname | wmname | mx
--------+--------+------------------------
canada | zoro | 2.0000000000000000
spain | luffy | 5.0000000000000000
spain | usopp | 5.0000000000000000
Rozwiązaniem, prawdopodobnie mniej eleganckim, aby pokazać tylko (cname, wmname)
krotki pasujące do wartości maksymalnej, jest:
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
[*]: Co ciekawe, mimo że spec pozwala na wybieranie pól niezagrupowanych, głównym silnikom wydaje się to nie podobać. Oracle i SQLServer po prostu na to nie pozwalają. Mysql domyślnie zezwalał na to, ale teraz Od 5.7 administrator musi ręcznie włączyć tę opcję (ONLY_FULL_GROUP_BY
) w konfiguracji serwera, aby ta funkcja była obsługiwana...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-17 06:34:26
W Postgres można również użyć specjalnego DISTINCT ON (expression)
składnia:
SELECT DISTINCT ON (cname)
cname, wmname, avg
FROM
makerar
ORDER BY
cname, avg DESC ;
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-01 09:05:35
Problem z określeniem pól niezagregowanych i niezagregowanych w selektach group by
polega na tym, że silnik nie ma możliwości poznania pola rekordu, które powinien zwrócić w tym przypadku. Najpierw? Ostatni? Zwykle nie ma zapisu, który w naturalny sposób odpowiada zagregowanemu wynikowi (min
i max
są wyjątkami).
Istnieje jednak obejście: zagreguj Wymagane pole. W posgres powinno to działać:
SELECT cname, (array_agg(wmname ORDER BY avg DESC))[1], MAX(avg)
FROM makerar GROUP BY cname;
Zauważ, że tworzy to tablicę wszystkich wnames, uporządkowane przez avg i zwraca pierwszy element (tablice w postgres są oparte na 1).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-28 00:19:39
Dla mnie nie chodzi o "powszechny problem agregacji", ale o niepoprawne zapytanie SQL. Jedna poprawna odpowiedź dla "wybierz maksymalną wartość avg dla każdego cname..."is
SELECT cname, MAX(avg) FROM makerar GROUP BY cname;
Wynik będzie:
cname | MAX(avg)
--------+---------------------
canada | 2.0000000000000000
spain | 5.0000000000000000
Ten wynik ogólnie odpowiada na pytanie "jaki jest najlepszy wynik dla każdej grupy?". Widzimy, że najlepszy wynik dla Hiszpanii to 5, a dla Kanady najlepszy wynik to 2. To prawda i nie ma błędu. Jeśli chcemy wyświetlić wmname Również, możemy musisz odpowiedzieć na pytanie: "Jaka jest reguła aby wybrać wmname z wynikowego zestawu?"Zmieńmy trochę dane wejściowe, aby wyjaśnić błąd:
cname | wmname | avg
--------+--------+-----------------------
spain | zoro | 1.0000000000000000
spain | luffy | 5.0000000000000000
spain | usopp | 5.0000000000000000
Jakiego wyniku oczekujesz po uruchomieniu tego zapytania: SELECT cname, wmname, MAX(avg) FROM makerar GROUP BY cname;
? Powinno być spain+luffy
Czy spain+usopp
? Dlaczego? Nie jest określone W zapytaniu jak wybrać "lepsze" wmname Jeśli kilka jest odpowiednich, więc wynik również nie jest określony. Dlatego interpreter SQL zwraca błąd-zapytanie nie jest zgadza się.
Innymi słowy, nie ma poprawnej odpowiedzi na pytanie "Kto jest najlepszy w grupie spain
?". Luffy nie jest lepszy od Usoppa, ponieważ usopp ma ten sam "wynik".
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-08 17:09:35
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;
Za pomocą rank()
funkcja okna :
SELECT cname, wmname, avg
FROM (
SELECT cname, wmname, avg, rank()
OVER (PARTITION BY cname ORDER BY avg DESC)
FROM makerar) t
WHERE rank = 1;
Uwaga
Każda z nich zachowa wiele wartości maksymalnych na Grupę. Jeśli chcesz tylko jeden rekord na Grupę, nawet jeśli jest więcej niż jeden rekord z avg równym max, powinieneś sprawdzić odpowiedź @ypercube.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-01 22:52:31
To wydaje się działać również
SELECT *
FROM makerar m1
WHERE m1.avg = (SELECT MAX(avg)
FROM makerar m2
WHERE m1.cname = m2.cname
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-30 21:52:21
Ostatnio napotkałem ten problem, próbując policzyć za pomocą case when
i stwierdziłem, że zmiana kolejności instrukcji which
i count
rozwiązuje problem:
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
Zamiast używać-w tym drugim, gdzie mam błędy, że jabłka i pomarańcze powinny pojawić się w funkcjach zbiorczych
CASE WHEN ((apples = 'TRUE' OR oranges 'TRUE') THEN COUNT(*) END) END AS fruit_counter
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-09 14:10:49