2009-10-20 16 views
37

Mam tabeli emp z następującą strukturę i dane żadne funkcje zagregowane:GROUP BY zachowań gdy są obecne w klauzuli SELECT

name dept salary 
----- ----- ----- 
Jack a  2 
Jill a  1 
Tom b  2 
Fred b  1 

Kiedy wykonać następujące SQL:

SELECT * FROM emp GROUP BY dept 

otrzymuję następujący wynik:

name dept salary 
----- ----- ----- 
Jill a  1 
Fred b  1 

Na jakiej podstawie serwer zdecydował wrócić Jill i F czerwony i wykluczyć Jacka i Toma?

Uruchomiłem to zapytanie w MySQL.

Uwaga 1: Wiem, że zapytanie nie ma sensu samo w sobie. Próbuję debugować problem ze scenariuszem "GROUP BY". Próbuję zrozumieć domyślne zachowanie w tym celu.

Uwaga 2: Jestem przyzwyczajony do pisania klauzuli SELECT tak samo jak klauzula GROUP BY (minus pola zagregowane). Kiedy natknąłem się na opisane powyżej zachowanie, zacząłem się zastanawiać, czy mogę na nich polegać w scenariuszach takich jak: wybierz wiersze z tabeli emp, w której pensja jest najniższa/najwyższa w dept. Np .: Instrukcje SQL, jak to działa na MySQL:

SELECT A.*, MIN(A.salary) AS min_salary FROM emp AS A GROUP BY A.dept 

nie mogę znaleźć dowolny materiał opisujący dlaczego takie SQL działa, co ważniejsze, czy mogę liczyć na takie zachowanie konsekwentnie. Jeśli jest to rzetelne zachowanie, mogę uniknąć takich zapytań jak:

SELECT A.* FROM emp AS A WHERE A.salary = ( 
      SELECT MAX(B.salary) FROM emp B WHERE B.dept = A.dept) 
+4

mógłbyś pisać jakim skutkiem masz nadzieję. GROUP BY zwraca jeden wiersz dla każdej unikalnej kombinacji kolumn GROUP BY. Ponieważ podałeś tylko jedną kolumnę, dept, zwróci ona tylko jeden wiersz na dept. Jeśli podasz w zapytaniu, czego potrzebujesz, ludzie mogą być w stanie pomóc więcej. –

+2

Powiedz nam, czego oczekujesz, a będziemy mogli Ci pomóc. – Marius

+1

moje przypuszczenie jest tym, który jest ostatnim wierszem każdej grupy –

Odpowiedz

33

Przeczytaj MySQL documentation w tym konkretnym punkcie.

W skrócie, MySQL umożliwia pominięcie niektórych kolumn z GROUP BY, dla celów krytyki ten działa tylko jeśli pominąć kolumny wszystkie mają taką samą wartość (w obrębie grupy), w przeciwnym razie wartości zwracane przez zapytanie są w istocie nieokreślone, zgodnie z tym, że inni zgadli w tym poście. Aby upewnić się, że dodanie klauzuli ORDER BY nie powtórzyłoby żadnej formy deterministycznego zachowania.

Chociaż nie jest to sedno problemu, ten przykład pokazuje, jak często używanie * zamiast jawnego wyliczania żądanych kolumn jest złym pomysłem.

Wyciąg z dokumentacji MySQL 5.0:

 
When using this feature, all rows in each group should have the same values 
for the columns that are omitted from the GROUP BY part. The server is free 
to return any value from the group, so the results are indeterminate unless 
all values are the same. 
+0

Zamierzałem zamieścić dokładnie to samo ... –

-1

Spróbuj użyć ORDER BY, aby wybrać żądany wiersz.

SELECT * FROM emp GROUP BY dept ORDER BY name ASC; 

zwróci następujący:

name dept salary 
----- ----- ----- 
jack a  2 
fred b  1 
+0

W moim przypadku ORDER BY nie ma znaczenia. Oczekiwałem takiego zachowania jako ORDER BY po zastosowaniu GROUP BY. –

4

O ile mi wiadomo, do swoich celów konkretnych wierszy zwracanych można concidered być przypadkowe.

tylko zamówieniowa następuje po GROUP BY odbywa

-1

Jeśli grupowanie przez dział ma znaczenie o innych danych? Wiem, że serwer Sql nie zezwoli na to zapytanie. Jeśli jest taka możliwość, mogą pojawić się inne problemy.

+1

Wiem, że ten SQL nie jest poprawny w Oracle i kilku innych bazach danych. –

1

Uważam, że najlepszym rozwiązaniem jest wzięcie pod uwagę tego typu zapytania obsługiwane. W większości innych systemów baz danych nie można uwzględniać kolumn, które nie znajdują się w klauzuli GROUP BY lub w funkcji agregującej w klauzulach HAVING, SELECT lub ORDER BY.

Zamiast tego, uważają, że zapytanie brzmi:

SELECT ANY(name), dept, ANY(salary) 
FROM emp 
GROUP BY dept; 

... ponieważ to jest to, co się dzieje.

Nadzieja to pomaga ....

0

myślę ANSI SQL wymaga select obejmuje tylko pola z klauzuli GROUP BY, plus funkcje agregujące. To zachowanie MySQL wygląda tak, jakby zwracało jakiś wiersz, być może ostatni odczytany serwer, lub dowolny wiersz, który miał pod ręką, ale nie polegaj na tym.

+0

O komentarzu Mariusza: (Nie mogę tego skomentować z powodu niskiej punktacji) Jak powiedzieli inni, Kolejność Działa na wynik Zgrupowania, nie ma sensu sortowanie wiersze, które zostaną zwinięte przez grupę.Zamiast tego możesz wybrać opcję MAX (nazwa), która faktycznie zwróci nazwisko, jeśli wiersze zostały uporządkowane alfabetycznie rosnąco. – Petruza

9

Trochę za późno, ale powiem to na przyszłość.

GROUP BY pobiera pierwszy wiersz z duplikatem i odrzuca wszystkie wiersze, które pasują do niego po zestawie wyników. Jeśli więc Jack i Tom mają ten sam dział, każdy, kto pojawi się jako pierwszy w zwykłym WYBORZE, będzie wynikiem tego rzędu w GROUP BY.

Jeśli chcesz kontrolować to, co pojawia się jako pierwsze na liście, musisz wykonać ZAMÓWIENIE PRZEZ. Jednak SQL nie zezwala na ORDER BY przed przyjściem GROUP BY, ponieważ spowoduje to zgłoszenie wyjątku. Najlepszym sposobem obejścia tego problemu jest wykonanie ORDER BY w podzapytaniu, a następnie GROUP BY w zapytaniu zewnętrznym. Oto przykład:

SELECT * FROM (SELECT * FROM emp ORDER BY name) as foo GROUP BY dept 

To jest najlepsza technika, którą udało mi się znaleźć. Mam nadzieję, że to pomoże komuś.

+0

Dzięki za to - super pomocny. Drogie subselect, ale wydaje się, że jest to jedyny sposób na zrobienie tego, co chciałbym zrobić "HAVING". – barclay

+6

Nie mogę znaleźć żadnego odniesienia, aby poprzeć twoje stwierdzenie, że "GROUP BY bierze pierwszy wiersz, który ma duplikat i odrzuca wszystkie wiersze, które pasują po nim w zestawie wyników." Przeciwnie, MySQL w szczególności wyraźnie stwierdza, że ​​wartość dla niezagregowanych kolumny są pobierane arbitralnie z dowolnego wiersza w grupie. – danorton

+2

To ** nie jest ** pomocne. Uważam to za pogarszającą sytuację. Instaed jednej niestandardowej funkcji, teraz używasz dwóch niestandardowych funkcji. Wyniki mogą być błędne i nie ma gwarancji, że otrzymasz pierwszy wynik na dział, uporządkowany według nazwy. –

2

można umieścić:

SET sql_mode = 'ONLY_FULL_GROUP_BY'

przed zapytaniu wymusić SQL GROUP BY standardowego zachowania

+0

W rzeczywistości jest to teraz domyślne w 2017 php ... – Sablefoste