Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Dwa pytania o GROUP BY
Forum PHP.pl > Forum > Bazy danych
Torcha
Możecie mi wyjaśnić, czym jest grupowanie (GROUP BY)? Z tego co zdążyłam zrozumiec, to jest to jakieś uproszczone sortowanie, polegające na tym, że wartości nie są porządkowane od najmniejszej do największej lub na odwrót, lecz jedynie "składane" "obok siebie" te, które są identyczne. Dobrze zrozumiałam? Mam jeszcze wątpliwości co do tego, kiedy grupowanie się odbywa. Wiem, że sortowanie odbywa się po całej tabeli i dopiero potem zwracana jest ustalona liczba rekordów (LIMIT). Nie wiem jednak, jak to jest z grupowaniem, ale wydaje mi się, że najpierw zostaje "wyciągnięta" żądana liczba rekordów, a dopiero później zostają one pogrupowane. Czy mam rację?

--
W trupa obrócę każdego, kto na mej drodze stanie.
Torcha

PS: Ja dopiero zaczynam poznawać mowę, którą posługują się bazy danych.

PS2: Dlaczego nie można pisać jako Gość? To absurd, że musiałam się zarejestrować tylko po to, by zadać dwa pytania.
msulik
Cytat
wydaje mi się, że najpierw zostaje "wyciągnięta" żądana liczba rekordów, a dopiero później zostają one pogrupowane.


Inaczej: najpierw zostają wyciągnięte rekordy (a nie ich liczba), a następnie zostają one pogrupowane według jakiejś kolumny.

W połączeniu z grupowaniem można użyć funkcji podsumowujących (agregujących) takich jak COUNT, AVG, SUM, MAX, MIN itp. Wówczas funkcje te będą dotyczyć rekordów o "wspólnej" wartości kolumny, którą grupowaliśmy.

Przykład:
[sql:1:7296594956]create table oceny
(
nazwisko varchar(255),
przedmiot varchar(255),
ocena int
);

insert into oceny (nazwisko, przedmiot, ocena) values
('Kowalski', 'mat', 3),('Nowak', 'mat', 3),('Iksinski', 'mat', 2),
('Kowalski', 'pol', 6),('Nowak', 'pol', 5),('Iksinski', 'pol', 5),
('Kowalski', 'geo', 3),('Nowak', 'geo', 4),('Iksinski', 'geo', 4),
('Kowalski', 'fiz', 4),('Nowak', 'fiz', 4),('Iksinski', 'fiz', 4),
('Kowalski', 'his', 2),('Nowak', 'his', 3),('Iksinski', 'his', 4),
('Kowalski', 'bio', 4),('Nowak', 'bio', 6),('Iksinski', 'bio', 4)
[/sql:1:7296594956]

Srednia kazdego ucznia (lista par: ("uczen", "srednia")):
[sql:1:7296594956]select nazwisko, avg(ocena) as srednia from oceny group by nazwisko;[/sql:1:7296594956]
To samo posortowane według nazwisk:
[sql:1:7296594956]select nazwisko, avg(ocena) as srednia from oceny group by nazwisko order by nazwisko;[/sql:1:7296594956]

Standardowe odchylenie ocen każdego z uczniów (lista par: ("uczeń", "st. odch. ocen ucznia"):
[sql:1:7296594956]select nazwisko, std(ocena) as odchylenie from oceny group by nazwisko order by nazwisko;[/sql:1:7296594956]

Aby otrzymać globalną srednią szkoły:
[sql:1:7296594956]select avg(ocena) as srednia from oceny;[/sql:1:7296594956]

Pierwsza dziesiątka uczniów:
[sql:1:7296594956]select nazwisko, avg(ocena) as srednia from oceny group by nazwisko order by srednia desc limit 10;[/sql:1:7296594956]
(oczywiście jest to prymitywne wybranie pierwszej dziesiątki; może być więcej uczniów, którzy mają średnią w pierwszej dziesiątce średnich).

Mozemy tez wyciagnac globalną średnią kazdego przedmiotu i utworzyć ranking od najlepszego przedmiotu do najgorszego:
[sql:1:7296594956]select przedmiot, avg(ocena) as srednia from oceny group by przedmiot order by srednia desc;[/sql:1:7296594956]
albo sprawdzić, ilu uczniów otrzymało każdą z ocen 1-6 z każdego przedmiotu (lista: ("ocena", "przedmiot", "liczba uczniów z taką oceną z tego przedmiotu"), z wyjątkiem ocen, które nie wystąpiły w danym przedmiocie):
[sql:1:7296594956]select przedmiot, ocena, count(nazwisko) as liczba_uczniow from oceny group by ocena, przedmiot;[/sql:1:7296594956]
To samo, posortowane malejąco według liczby uczniów:
[sql:1:7296594956]select przedmiot, ocena, count(nazwisko) as liczba_uczniow from oceny group by ocena, przedmiot order by liczba_uczniow desc;[/sql:1:7296594956]
To samo po odrzuceniu tych ocen, które w danym przedmiocie wystąpiły pojedynczo (klauzula HAVING):
[sql:1:7296594956]select przedmiot, ocena, count(nazwisko) as liczba_uczniow from oceny group by ocena, przedmiot
HAVING liczba_uczniow > 1 order by liczba_uczniow desc[/sql:1:7296594956]





Prosty przykład 2 - zapytanie do dwóch tabel:
[sql:1:7296594956]create table dzialy
(
id_dzialu int,
nazwa varchar(255)
);
create table pracownicy
(
nazwisko varchar(255),
id_dzialu int,
zarobki int
);
insert into dzialy values (1, 'logistyka'),(2, 'kadry'),(3, 'produkcja');
insert into pracownicy values ('aaa', 1, 123),('bbb', 2, 421),('ccc', 1, 45),('ddd', 3, 531),('eee', 2, 52);
[/sql:1:7296594956]

Srednie zarobki w kazdym dziale (lista par: ("nazwa działu", "zarobki")):
[sql:1:7296594956]select dzialy.nazwa AS nazwa_dzialu, avg(pracownicy.zarobki) AS srednie_zarobki from dzialy, pracownicy where dzialy.id_dzialu = pracownicy.id_dzialu group by dzialy.id_dzialu [/sql:1:7296594956]

To samo co wyzej plus liczba pracownikow w danym dziale:
[sql:1:7296594956]select dzialy.nazwa AS nazwa_dzialu, avg(pracownicy.zarobki) AS srednie_zarobki, count(pracownicy.nazwisko) AS liczba_pracownikow from dzialy, pracownicy where dzialy.id_dzialu = pracownicy.id_dzialu group by pracownicy.id_dzialu [/sql:1:7296594956]

Pamiętaj o tym, że MySQL (inne systemy raczej też) nie lubi, jeśli po nazwach funkcji występuje odstęp.

Grupowanie jest trudne do wyjaśnienia, sam miewam problemy z poprawnym zastosowaniem w bardziej rozbudowanych zapytaniach, zwłaszcza, jeśli zapytanie odbywa się do wielu tabel. Pozostaje eksperymentować.
Torcha
Cytat
Cytat
wydaje mi się, że najpierw zostaje "wyciągnięta" żądana liczba rekordów, a dopiero później zostają one pogrupowane.


Inaczej: najpierw zostają wyciągnięte rekordy (a nie ich liczba), a następnie zostają one pogrupowane według jakiejś kolumny.

Pisząc "żądana liczba rekordów", miałam na myśli "rekordy w żądanej liczbie (ilości)". W pytaniu chodziło mi o kolejność działania: czy najpierw wszystkie rekordy z tabeli zostają pogrupowane według określonej kolumny, natomiast w dalszej kolejności ograniczona zostaje ich ilość (jak przy sortowaniu ORDER BY), czy sytuacja jest odwrotna - wyciągane są rekordy w żądanej ilości (teraz chyba zrozumiale napisałam winksmiley.jpg) i pogrupowany zostaje tylko ten "zestaw wynikowy"?

Cytat
W połączeniu z grupowaniem można użyć funkcji podsumowujących (agregujących) takich jak COUNT, AVG, SUM, MAX, MIN itp. Wówczas funkcje te będą dotyczyć rekordów o "wspólnej" wartości kolumny, którą grupowaliśmy.

Czy przy grupowaniu zakładany jest jakiś porządek, czy kolejność następujących po sobie grup jest zależna od pierwszego wystąpienia pola należącego do danej grupy, a każdy następny rekord z polem o tej samej wartości w tej samej kolumnie jest jedynie "dociągany" do tych "pierwszych"?

Cytat
Grupowanie jest trudne do wyjaśnienia, sam miewam problemy z poprawnym zastosowaniem w bardziej rozbudowanych zapytaniach, zwłaszcza, jeśli zapytanie odbywa się do wielu tabel. Pozostaje eksperymentować.

Dziękuję serdecznie za liczne przykłady. Spędziłam pare godzin z googlami na poszukiwaniu jakichkolwiek konkretnych informacji, ale nie znalazłam wyczerpującego opisu działania klauzuli GROUP BY. W istocie dopiero twoje przykłady przybliżyły mi działanie funkcji agregujących. Szkoda jednak, że nie wyjaśniłeś mi, w jakiej kolejności to wszystko się odbywa: funkcje agregujące i grupowanie działają na wszystkich rekordach z tabeli czy tylko tych ograniczonych klauzulą LIMIT, która wówczas musiałaby zostać "wykonywana" przed klauzulą GROUP BY? Wiesz, jaka jest kolejność? Na koniec chciałabym wiedzieć, jaka jest zależność pomiędzy grupowaniem a sortowaniem - jak się ma jedno do drugiego?
uboottd
Mozesz przyjac ze klauzule sa wykonywane w kolejnosci zapisu. Czyli najpierw rekordy zostana zgrupowane w pojedyncze wiersze wedlug warunku po GROUP BY a potem tak otrzymany wynik zostanie pociety wedlug wartosci po LIMIT.

W obrebie grupy kolejnosc nie ma znaczenia i jest nieokreslona. Wedlug standartu mozesz odwolywac sie tylko funkcjami agregujacymi (czyli SUM(), AVG(), MIN() etc.) do wartosci z grupowanych wierszy, a one zawsze biora pod uwage wszystkie wartosci w danej grupie. Czyli zapis

[sql:1:be520a6083]
SELECT wiek, numer_buta, sum(zarobek) FROM pracownicy GROUP BY wiek;
[/sql:1:be520a6083]

jest nielegalny - wiek mozesz pobrac (bo w obrebie grupy jest staly wiec nie tu niejednoznacznosci, jednakze pracownicy o tym samym wieku moga miec rozne rozmiary buta, i o numer ktorego moze chodzic ?

Jednakze MySQL to w pewnym sensie umozliwia - w nim ten zapis jest prawidlowy, ale numer_buta dostaniesz ktoregos, przypadkowego pracownika - baza nieokresla ktory wiersz z grupy zostanie wykorzystany do tego. Jest to przydatne jak skad inad wiesz ze ta wartosc i tak jest stala bo cos tam.
msulik
Cytat
Czy przy grupowaniu zakładany jest jakiś porządek, czy kolejność następujących po sobie grup jest zależna od pierwszego wystąpienia pola należącego do danej grupy, a każdy następny rekord z polem o tej samej wartości w tej samej kolumnie  jest jedynie "dociągany" do tych "pierwszych"?
Nie wiem dokładnie jak to jest realizowane, ale wydaje mi się, że nie jest ważna kolejność grupowania - dla funkcji agregujących nie ma to znaczenia. Najpierw zostają utworzone grupy, a następnie na każdej z tych grup działa funkcja agregująca. Prawdopodobnie optymalizator sprawia, że nie są zapamiętywane dokładnie wszystke rekordy, a jedynie z każdego kolejnego rekordu wyciągane są potrzebne informacje, które wykorzystujemy gdy używamy funkcji agregujących. Nie ma bowiem sensu zapamiętywać w grupach wszystkich rekordów, jeśli chodzi nam tylko np. o sumę (którą można policzyć liniowo) czy średnią wartości (średnia to iloraz sumy wartości przez ich ilość, a więc SUM()/COUNT() ).

Cytat
(...) w jakiej kolejności to wszystko się odbywa: funkcje agregujące i grupowanie działają na wszystkich rekordach z tabeli czy tylko tych ograniczonych klauzulą LIMIT, która wówczas musiałaby zostać "wykonywana" przed klauzulą GROUP BY? Wiesz, jaka jest kolejność? Na koniec chciałabym wiedzieć, jaka jest zależność pomiędzy grupowaniem a sortowaniem - jak się ma jedno do drugiego?


Rozpatrzmy składnię SELECT'a:
[sql:1:ccc8a63cae]select lista_wybierania from lista_tabel where wyrażenie group by lista_kolumn
having wyrażenie order by lista kolumn limit opcje_limit[/sql:1:ccc8a63cae]

Kolejność wykonywania:
WHERE -> GROUP BY -> funkcje agregujące -> HAVING -> ORDER BY -> LIMIT

Mimo, że HAVING jest wykonywane PO wybraniu rekordów, to jak mówi dokumentacja MySQL, jeśli użyjemy HAVING oraz jeśli nie używamy GROUP BY i funkcji agregujących, to warunek HAVING zostanie dołączony do WHERE w celu zoptymalizowania warunku:
Cytat
HAVING is merged with WHERE if you don't use GROUP BY or group functions (COUNT(), MIN()...).
a także niektóre warunki, które występują w HAVING mogą zostać sprawdzone:
Cytat
Before each record is output, those that do not match the HAVING clause are skipped
(domyślam się, że chodzi o takie warunki, które nie wymagają odwołania się do wyniku grupowania).
uboottd
Cytat
a także niektóre warunki, które występują w HAVING mogą zostać sprawdzone:
Cytat
Before each record is output, those that do not match the HAVING clause are skipped
(domyślam się, że chodzi o takie warunki, które nie wymagają odwołania się do wyniku grupowania).


Nie w Moskwie a w Leningradzie,
Rekordy nie warunki,
I nie moga a sa pomijane!
smile.gif
I wynika to z definicji klauzuli HAVING.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.