Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Grupowanie po posortowaniu malejąco
phpion
post 26.05.2017, 08:19:45
Post #1





Grupa: Moderatorzy
Postów: 6 070
Pomógł: 860
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




Tak z czystej ciekawości. Czy ktoś jest w stanie odpowiedzieć dlaczego poniższe ogólnie działa, ale nie działa na komputerze kolegi? smile.gif Jest to Mac z zainstalowanym XAMPPem. Poniżej wklejam całość testu:
  1. CREATE TABLE `tabela` (
  2. `id` INT NOT NULL,
  3. `point_id` INT NOT NULL,
  4. `date` DATE NOT NULL,
  5. `value` INT NOT NULL,
  6. PRIMARY KEY (`id`));
  7.  
  8. INSERT INTO tabela (id, point_id, date, value) VALUES
  9. (1, 1, '2017-01-01', 1),
  10. (2, 1, '2017-01-02', 2),
  11. (3, 1, '2017-01-03', 3),
  12.  
  13. (4, 2, '2017-01-03', 1),
  14. (5, 2, '2017-01-02', 2),
  15. (6, 2, '2017-01-01', 3)
  16. ;
  17.  
  18. SELECT * FROM (
  19. SELECT
  20. *
  21. FROM
  22. tabela
  23. ORDER BY
  24. id DESC
  25. ) AS t GROUP BY point_id;

Wszędzie poza komputerem kolegi smile.gif zapytanie zwraca rekordy o ID 3 i 6, natomiast u niego: 1 i 4 tak jakby sortowanie w podzapytaniu nie było uwzględnione. Czy ktoś wie gdzie leży przyczyna? Jakieś ustawienia konfiguracyjne?
Go to the top of the page
+Quote Post
trueblue
post 26.05.2017, 08:49:45
Post #2





Grupa: Zarejestrowani
Postów: 6 761
Pomógł: 1822
Dołączył: 11.03.2014

Ostrzeżenie: (0%)
-----


Dlaczego jest różnica nie wiem, może wynika z różnych wersji MySQL.
Ale przede wszystkim:
Cytat
If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard SQL use of GROUP BY permits the select list, HAVING condition, or ORDER BY list to refer to nonaggregated columns even if the columns are not functionally dependent on GROUP BY columns. This causes MySQL to accept the preceding query. In this case, the server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate, which is probably not what you want.

Z czego wynika, że wybrane wartości z niegrupowanych kolumn są "losowe".
Nie możesz opierać rezultatu obecnego zapytania na podstawie tego, że u większości zwraca poprawny wynik. Nawet jakby u wszystkich zwracało taki sam.
Jeśli chcesz otrzymywać taki rezultat, musisz przebudować zapytanie.


--------------------
Go to the top of the page
+Quote Post
pmir13
post 31.05.2017, 23:17:45
Post #3





Grupa: Zarejestrowani
Postów: 282
Pomógł: 89
Dołączył: 12.04.2011

Ostrzeżenie: (0%)
-----


Wartości z pól SELECT przy użyciu GROUP BY nigdy nie możemy być pewni o ile nie są tymi, po których grupujemy (i wtedy oczywiście tylko jedna wartość każdej grupie jest możliwa) lub nie są w funkcjach agregujących, czyli liczących coś ze wszystkich wartości w grupie, np max, avg, sum i tak dalej.
W innych systemach baz danych np oracle czy sqlserver, jeśli spróbowalibyśmy do selecta wrzucić inne pole w ogóle by to nie przeszło i dostalibyśmy błąd składni. Mysql na to pozwala jeśli nie mamy w konfiguracji sql_mode ONLY_FULL_GORUP_BY, zakładając, że pisząc SQL wiemy co robimy i dodatkowe pola mają w każdej grupie te same wartości, co się może zdarzyć na przykład po złączeniu w relacji jeden do wielu grupując po id z pierwszej tabeli. Wtedy wszystkie kolumny z tej pierwszej tabeli są identyczne a tylko te z dołączonej się różnią. Wtedy uniknięcie jeszcze jednego złączenia wykorzystując "niepoprawną" składnię pomaga w wydajności.

Ogólnie ten problem można zobrazować na przykładzie:
  1. SELECT imie, nazwisko, max(wzrost) FROM uczniowie GROUP BY klasa

Intuicyjnie można pomyśleć, że chcemy imię i nazwisko najwyższego ucznia, ale systemy baz danych rozumieją to jako:
(mysql) - podaj jakieś imię, jakieś nazwisko i najwyższy wzrost z każdej klasy
(inne) - imie i nazwisko nie mogą być w select, więc syntax error
Jedyne pola jakie poprawnie mogłyby się znaleźć w select to klasa bo po niej grupujemy oraz max(wzrost) bo max jest funkcją agregującą.
I nawet w mysql jeśli sobie wcześniej posortujemy w podzapytaniu według wzrostu nic to nie zmieni, bo wciąż mysql poda nam JAKIEŚ imię i JAKIEŚ nazwisko z każdej grupy a nie ma możliwości zażyczenia sobie pierwszego. W szczególności dostaniemy to imię oraz to nazwisko, do którego mysql najszybciej się dostanie, co może zależeć od wielu czynników, np rodzaju dysku, ewentualnej fragmentacji, czy dostęp będzie sekwencyjny czy nie, czy mamy jakieś indeksy, przez które mysql może inaczej dane na dysku rozłożyć, jak sobie optymalizator wybierze metodę grupowania i tak dalej. Wersja mysql też może mieć swój udział.
Zresztą co by było gdybyśmy na przykład chcieli:
  1. SELECT imie, nazwisko, max(wzrost), min(wzrost) FROM uczniowie GROUP BY klasa

Co by nam wtedy wewnętrzne sortowanie dało? W dwie strony naraz przecież nie da rady.
Jeśli więc chcemy znaleźć imię i nazwisko najwyższego delikwenta musimy użyć:
  1. SELECT imie, nazwisko, klasa, wzrost FROM uczniowie u
  2. JOIN ( SELECT klasa, max(wzrost) AS najwyzszy FROM uczniowie GROUP BY klasa ) u_max
  3. ON u.klasa = u_max.klasa AND u.wzrost = u_max.najwyzszy

Co dla bazy jest jasne:
(wszystkie) - znajdź najwyższy wzrost dla każdej klasy w podzapytaniu i podaj którym uczniom w tych klasach ten wzrost odpowiada
Oczywiście może się zdarzyć, że więcej niż jedna osoba ma ten sam wzrost i musimy pomyśleć co z tym dalej, czy chcemy wszystkich, czy może tylko jednego i trzeba pomyśleć jak tego jednego dalej wybrać.
Ogólnie jednak znajdowanie najwyższych wartości w grupie to powszechnie znany problem wynikający ze specyfiki języka SQL, a na stackoverflow nawet doczekał się własnego tagu [greatest-n-per-group].
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 23.04.2024 - 07:26