Pierwszą rzeczą, którą należy wyjaśnić, jest to, że SQL to nie MySQL.
W standardowym SQL nie jest dozwolone grupowanie według podzbioru pól niezagregowanych. Powód jest bardzo prosty. Załóżmy, że uruchamiam to zapytanie:
SELECT color, owner_name, COUNT(*) FROM cars
GROUP BY color
To zapytanie nie miałoby sensu. Nawet próba wyjaśnienia byłaby niemożliwa. Na pewno jest to wybór kolorów i liczenie ilości samochodów na kolor. Jednak dodaje również owner_name
pola i może być wielu właścicieli dla danego koloru, tak jak w przypadku White
kolor. Więc jeśli może być wiele owner_name
wartości dla pojedynczego color
które jest jedynym polem w GROUP BY
klauzula... to która owner_name
zostanie zwrócony?
Jeśli konieczne jest zwrócenie owner_name
wtedy należy dodać jakieś kryteria, aby wybrać tylko jedno z nich, np. pierwsze z nich alfabetycznie, czyli w tym przypadku będzie to John
. To kryterium spowoduje dodanie funkcji agregującej MIN(owner_name)
a wtedy zapytanie znów nabierze sensu, ponieważ będzie grupować przynajmniej wszystkie niezagregowane pola w instrukcji select.
Jak widać, istnieje jasny i praktyczny powód, dla którego standardowy SQL jest nieelastyczny w grupowaniu. Gdyby tak nie było, możesz napotkać niezręczne sytuacje, w których wartość kolumny będzie nieprzewidywalna, a to nie jest miłe słowo, szczególnie jeśli uruchamiane zapytanie pokazuje transakcje na Twoim koncie bankowym.
Powiedziawszy to, dlaczego MySQL miałby zezwalać na zapytania, które mogą nie mieć sensu? A co gorsza, błąd w powyższym zapytaniu mógłby zostać po prostu wykryty składniowo! Krótka odpowiedź brzmi:wydajność. Długa odpowiedź brzmi, że istnieją pewne sytuacje, w których na podstawie relacji danych otrzymanie nieprzewidywalnej wartości z grupy da w wyniku przewidywalną wartość.
Jeśli jeszcze tego nie rozgryzłeś, jedynym sposobem, w jaki możesz przewidzieć wartość, jaką uzyskasz z pobrania nieprzewidywalnego elementu z grupy, będzie stwierdzenie, że wszystkie elementy w grupie są takie same. Jasnym przykładem tej sytuacji jest przykładowe zapytanie w tym samym pytaniu. Zobacz, jak owner_id
i owner_name
odnosi się do tabeli. Oczywiste jest, że podano dowolny owner_id
, np. 2
, możesz mieć tylko jedną odrębną owner_name
. Nawet mając wiele wierszy, wybierając dowolny, otrzymasz Mike
jako wynik. W formalnym żargonie bazy danych można to wyjaśnić jako owner_id
funkcjonalnie określa owner_name
.
Przyjrzyjmy się bliżej temu w pełni działającemu zapytaniu MySQL:
SELECT owner_id, owner_name, COUNT(*) total FROM cars
GROUP BY owner_id
Podano dowolny owner_id
to zwróciłoby to samo owner_name
, więc dodanie go do GROUP BY
klauzula nie spowoduje zwrócenia większej liczby wierszy. Nawet dodanie zagregowanej funkcji MAX(owner_name)
nie spowoduje zwrócenia mniejszej liczby wierszy. Otrzymane dane będą dokładnie takie same. W obu przypadkach zapytanie zostałoby natychmiast przekształcone w legalne standardowe zapytanie SQL, ponieważ przynajmniej wszystkie pola niezagregowane zostałyby pogrupowane według. Istnieją więc 3 podejścia do uzyskania tych samych wyników.
Jednak, jak wspomniałem wcześniej, to niestandardowe grupowanie ma przewagę wydajnościową. Możesz sprawdzić ten tak niedoceniany link w którym jest to wyjaśnione bardziej szczegółowo, ale zamierzam przytoczyć najważniejszą część:
Warto wspomnieć, że wyniki niekoniecznie są złe ale raczej nieokreślony . Innymi słowy, uzyskanie oczekiwanych wyników nie oznacza, że napisałeś właściwe zapytanie. Napisanie właściwego zapytania zawsze da oczekiwane rezultaty.
Jak widać, warto zastosować to rozszerzenie MySQL do GROUP BY
klauzula. W każdym razie, jeśli nie jest to jeszcze w 100% jasne, istnieje praktyczna zasada, która zapewni, że grupowanie będzie zawsze poprawne:Zawsze grupuj przynajmniej według wszystkich niezagregowanych pól w klauzuli select . W pewnych sytuacjach możesz marnować kilka cykli procesora, ale jest to lepsze niż zwracanie nieokreślonego wyniki. Jeśli nadal boisz się nieprawidłowego grupowania, zmień ONLY_FULL_GROUP_BY
Tryb SQL może być ostatecznością :)
Oby Twoje grupowanie było poprawne i wydajne... a przynajmniej poprawne.