MySQL
Błąd 1055: ONLY_FULL_GROUP_BY: coś nie znajduje się w klauzuli GROUP BY ...
Szukaj…
Wprowadzenie
GROUP BY
, a przynajmniej utrudnił wypalenie programistom piszącym zapytania.
Uwagi
Od dłuższego czasu MySQL zawiera znane niestandardowe rozszerzenie GROUP BY
, które pozwala na zachowanie nieparzystych w imię wydajności. To rozszerzenie pozwoliło niezliczonym programistom na całym świecie używać GROUP BY
w kodzie produkcyjnym bez pełnego zrozumienia tego, co robili.
W szczególności złym pomysłem jest użycie SELECT *
w zapytaniu GROUP BY
, ponieważ standardowa klauzula GROUP BY
wymaga wyliczenia kolumn. Niestety wielu programistów to zrobiło.
Przeczytaj to. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
Zespół MySQL próbował naprawić tę pomyłkę bez zepsucia kodu produkcyjnego. sql_mode
flagę sql_mode
w 5.7.5 o nazwie ONLY_FULL_GROUP_BY
aby wymusić standardowe zachowanie. W najnowszej wersji domyślnie włączyli tę flagę. Po uaktualnieniu lokalnego MySQL do 5.7.14 flaga została włączona, a kod produkcyjny, w zależności od starego rozszerzenia, przestał działać.
Jeśli ostatnio zacząłeś otrzymywać błędy 1055, jakie są twoje możliwości?
- naprawić niepoprawne zapytania SQL lub poproś ich autorów o zrobienie tego.
- wycofaj się z powrotem do wersji zgodnej z MySQL z używanym oprogramowaniem.
- zmień
sql_mode
swojego serwera, aby pozbyć się nowo ustawionego trybuONLY_FULL_GROUP_BY
.
Możesz zmienić tryb, wykonując polecenie SET
.
SET sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
powinien zrobić to, jeśli zrobisz to zaraz po tym, jak aplikacja połączy się z MySQL.
Lub możesz znaleźć plik init w swojej instalacji MySQL , zlokalizować sql_mode=
i zmienić go tak, aby ONLY_FULL_GROUP_BY
, i zrestartuj serwer.
Używanie i niewłaściwe korzystanie z GROUP BY
SELECT item.item_id, item.name, /* not SQL-92 */
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
pokaże wiersze w tabeli o nazwie item
i pokaże liczbę powiązanych wierszy w tabeli o nazwie uses
. Działa to dobrze, ale niestety nie jest to standardowy SQL-92.
Dlaczego nie? ponieważ klauzula SELECT
(i klauzula ORDER BY
) w zapytaniach GROUP BY
muszą zawierać kolumny, które są
- wymienione w klauzuli
GROUP BY
lub - funkcje agregujące, takie jak
COUNT()
,MIN()
i tym podobne.
W tym przykładzie klauzula SELECT
wspomina item.name
, kolumnę, która nie spełnia żadnego z tych kryteriów. MySQL 5.6 i wcześniejsze odrzucą to zapytanie, jeśli tryb SQL zawiera ONLY_FULL_GROUP_BY
.
To przykładowe zapytanie może być zgodne ze standardem SQL-92, zmieniając klauzulę GROUP BY
, w ten sposób.
SELECT item.item_id, item.name,
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id, item.name
Późniejszy standard SQL-99 pozwala instrukcji SELECT
na pominięcie niezagregowanych kolumn w kluczu grupy, jeśli DBMS może udowodnić funkcjonalną zależność między nimi a kolumnami klucza grupy. Ponieważ item.name
jest funkcjonalnie zależny od item.item_id
, początkowy przykład to poprawny SQL-99. MySQL zyskał funkcjonalnego kontrolera zależności w wersji 5.7. Oryginalny przykład działa pod ONLY_FULL_GROUP_BY
.
Niewłaściwe użycie GROUP BY w celu zwrócenia nieprzewidzianych rezultatów: prawo Murphy'ego
SELECT item.item_id, uses.category, /* nonstandard */
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
pokaże wiersze w tabeli o nazwie item i pokaże liczbę powiązanych wierszy w tabeli o nazwie use. Wyświetli również wartość kolumny o nazwie uses.category
.
To zapytanie działa w MySQL (zanim pojawiła się flaga ONLY_FULL_GROUP_BY
). Wykorzystuje niestandardowe rozszerzenie MySQL do GROUP BY
.
Ale zapytanie ma problem: jeśli kilka wierszy w tabeli uses
pasuje do warunku ON
w klauzuli JOIN
, MySQL zwraca kolumnę category
tylko z jednego z tych wierszy. Który rząd? Autor zapytania i użytkownik aplikacji nie dowie się o tym wcześniej. Formalnie rzecz biorąc, jest to nieprzewidywalne : MySQL może zwrócić dowolną wartość.
Nieprzewidywalny jest losowy, z jedną znaczącą różnicą. Można się spodziewać od czasu do czasu losowego wyboru. Dlatego jeśli wybór był losowy, można go wykryć podczas debugowania lub testowania. Nieprzewidywalny wynik jest gorszy: MySQL zwraca ten sam wynik za każdym razem, gdy używasz zapytania, dopóki tak nie jest. Czasami jest to nowa wersja serwera MySQL, która powoduje inny wynik. Czasami problem stanowi rosnący stół. Co może pójść nie tak, pójdzie nie tak, a kiedy się tego nie spodziewasz. To się nazywa prawo Murphy'ego .
Zespół MySQL pracował nad tym, aby trudniej było programistom popełnić ten błąd. Nowsze wersje MySQL w sekwencji 5,7 mają sql_mode
flagę zwaną ONLY_FULL_GROUP_BY
. Po ustawieniu tej flagi serwer MySQL zwraca błąd 1055 i odmawia uruchomienia tego rodzaju zapytania.
Niewłaściwe użycie GROUP BY za pomocą SELECT * i jak to naprawić.
Czasami zapytanie wygląda tak, z *
w klauzuli SELECT
.
SELECT item.*, /* nonstandard */
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
Takie zapytanie musi zostać refaktoryzowane, aby było zgodne ze standardem ONLY_FULL_GROUP_BY
.
Aby to zrobić, musimy podkwerenda który używa GROUP BY
poprawnie zwrócić number_of_uses
wartość dla każdego item_id
. To podzapytanie jest krótkie i słodkie, ponieważ wystarczy spojrzeć na tabelę uses
.
SELECT item_id, COUNT(*) number_of_uses
FROM uses
GROUP BY item_id
Następnie możemy połączyć to podzapytanie z tabelą item
.
SELECT item.*, usecount.number_of_uses
FROM item
JOIN (
SELECT item_id, COUNT(*) number_of_uses
FROM uses
GROUP BY item_id
) usecount ON item.item_id = usecount.item_id
Dzięki temu klauzula GROUP BY
jest prosta i poprawna, a także pozwala nam użyć *
specyfikatora.
Uwaga: jednak mądrzy programiści unikają w każdym przypadku użycia specyfikatora *
. Zwykle lepiej jest wymienić kolumny, które chcesz w zapytaniu.
DOWOLNA WARTOŚĆ()
SELECT item.item_id, ANY_VALUE(uses.tag) tag,
COUNT(*) number_of_uses
FROM item
JOIN uses ON item.item_id, uses.item_id
GROUP BY item.item_id
pokazuje wiersze w tabeli o nazwie item
, liczbę powiązanych wierszy i jedną z wartości w powiązanej tabeli o nazwie uses
.
Możesz myśleć o tej funkcji ANY_VALUE()
jako dziwnym rodzaju funkcji agregującej. Zamiast zwracać liczbę, sumę lub maksimum, instruuje serwer MySQL, aby arbitralnie wybrał jedną wartość z danej grupy. Jest to sposób obejścia błędu 1055.
Zachowaj ostrożność, używając ANY_VALUE()
w zapytaniach w aplikacjach produkcyjnych.
To naprawdę powinno się nazywać SURPRISE_ME()
. Zwraca wartość jakiegoś wiersza w grupie GROUP BY. Który wiersz zwraca, jest nieokreślony. Oznacza to, że wszystko zależy od serwera MySQL. Formalnie zwraca nieprzewidywalną wartość.
Serwer nie wybiera losowej wartości, jest gorzej. Zwraca tę samą wartość za każdym razem, gdy uruchamiasz zapytanie, dopóki tak nie jest. Może się zmieniać, lub nie, gdy stół powiększy się lub zmniejszy, albo gdy serwer będzie miał więcej lub mniej pamięci RAM, albo gdy zmieni się wersja serwera, lub gdy Mars będzie w trybie wstecznym (cokolwiek to znaczy), lub bez żadnego powodu.
Zostałeś ostrzeżony.