Następujące zapytanie:
SELECT
year, id, rate
FROM h
WHERE year BETWEEN 2000 AND 2009
AND id IN (SELECT rid FROM table2)
GROUP BY id, year
ORDER BY id, rate DESC
daje:
year id rate
2006 p01 8
2003 p01 7.4
2008 p01 6.8
2001 p01 5.9
2007 p01 5.3
2009 p01 4.4
2002 p01 3.9
2004 p01 3.5
2005 p01 2.1
2000 p01 0.8
2001 p02 12.5
2004 p02 12.4
2002 p02 12.2
2003 p02 10.3
2000 p02 8.7
2006 p02 4.6
2007 p02 3.3
Chciałbym tylko 5 najlepszych wyników dla każdego identyfikatora:
2006 p01 8
2003 p01 7.4
2008 p01 6.8
2001 p01 5.9
2007 p01 5.3
2001 p02 12.5
2004 p02 12.4
2002 p02 12.2
2003 p02 10.3
2000 p02 8.7
Czy można to zrobić za pomocą jakiegoś modyfikatora typu LIMIT, który działa w ramach GROUP BY?
sql
mysql
greatest-n-per-group
ranking
Studnie
źródło
źródło
LIMIT
klauzuli. Oto artykuł, który szczegółowo wyjaśnia problem: Jak wybrać pierwszy / najmniejszy / maksymalny wiersz na grupę w SQL To dobry artykuł - wprowadza eleganckie, ale naiwne rozwiązanie problemu „Top N na grupę”, a następnie stopniowo poprawia to.Odpowiedzi:
Możesz użyć funkcji zagregowanej GROUP_CONCAT, aby uzyskać wszystkie lata w jednej kolumnie, pogrupowane
id
i uporządkowane wedługrate
:Wynik:
A potem możesz użyć FIND_IN_SET , która zwraca pozycję pierwszego argumentu wewnątrz drugiego, np.
Używając kombinacji
GROUP_CONCAT
iFIND_IN_SET
oraz filtrując według pozycji zwróconej przez find_in_set, możesz użyć tego zapytania, które zwraca tylko pierwsze 5 lat dla każdego identyfikatora:Zobacz skrzypce tutaj .
Należy pamiętać, że jeśli więcej niż jeden wiersz może mieć tę samą stawkę, należy rozważyć użycie GROUP_CONCAT (DISTINCT stawka ORDER BY według stawki) w kolumnie stawki zamiast kolumny roku.
Maksymalna długość ciągu zwracanego przez GROUP_CONCAT jest ograniczona, więc działa to dobrze, jeśli trzeba wybrać kilka rekordów dla każdej grupy.
źródło
SET SESSION group_concat_max_len = <maximum length>;
W przypadku PO, nie problem (ponieważ domyślnie jest to 1024), ale na przykład group_concat_max_len powinien wynosić co najmniej 25: 4 (maks. długość ciągu roku) + 1 (znak separatora), razy 5 (pierwsze 5 lat). Ciągi są raczej obcinane niż zgłaszane błędy, więc należy uważać na ostrzeżenia takie jak1054 rows in set, 789 warnings (0.31 sec)
.FIND_IN_SET()
. Próbowałem,FIND_IN_SET() =2
ale nie pokazałem wyników zgodnie z oczekiwaniami.W pierwotnym zapytaniu użyto zmiennych użytkownika i
ORDER BY
tabel pochodnych; zachowanie obu dziwactw nie jest gwarantowane. Zmieniona odpowiedź w następujący sposób.W MySQL 5.x można użyć rangi biedaka nad partycją, aby osiągnąć pożądany rezultat. Wystarczy zewnętrznie połączyć tabelę ze sobą i dla każdego wiersza policzyć liczbę wierszy mniejszą od niego. W powyższym przypadku mniejszy wiersz to ten o wyższej stawce:
Demo i wynik :
Pamiętaj, że jeśli stawki były powiązane, na przykład:
Powyższe zapytanie zwróci 6 wierszy:
Zmień, aby
HAVING COUNT(DISTINCT l.rate) < 5
uzyskać 8 wierszy:Lub zmień, aby
ON t.id = l.id AND (t.rate < l.rate OR (t.rate = l.rate AND t.pri_key > l.pri_key))
uzyskać 5 wierszy:W MySQL 8 lub później po prostu użyć
RANK
,DENSE_RANK
lubROW_NUMBER
funkcje:źródło
WHERE rank <=5
? Po raz pierwszy nie dostaję 5 wierszy z każdego identyfikatora, ale potem mogę uzyskać, jak powiedziałeś.SET
oświadczenia (patrz pierwsze zapytanie). Czy to jest to konieczne.ORDER BY
tabela pochodna może i często będzie ignorowana. To pokonuje cel. Wydajne grupowo można znaleźć tutaj .ORDER BY
w dostarczanych / takich zapytaniach. To jest powód, dla którego nowoczesne wersje MySQL / MariaDB ignorująORDER BY
podzapytanie bez użyciaLIMIT
, wierzę, że normy ANSI / ISO SQL 2008/2011/2016 dopuszczająORDER BY
dostarczanie / podkwerendy, gdy są używane w połączeniu zFETCH FIRST n ROWS ONLY
Dla mnie coś takiego
działa świetnie. Bez skomplikowanego zapytania.
na przykład: zdobądź 1 miejsce dla każdej grupy
źródło
Nie, nie możesz OGRANICZAĆ podkwerend arbitralnie (możesz to zrobić w ograniczonym zakresie w nowszych MySQL, ale nie dla 5 wyników na grupę).
Jest to zapytanie grupowe o maksymalnej liczbie, które nie jest trywialne w SQL. Istnieją różne sposoby rozwiązania tego problemu, które mogą być bardziej wydajne w niektórych przypadkach, ale w przypadku top-n ogólnie będziesz chciał spojrzeć na odpowiedź Billa na podobne poprzednie pytanie.
Podobnie jak w przypadku większości rozwiązań tego problemu, może zwrócić więcej niż pięć wierszy, jeśli istnieje wiele wierszy o tej samej
rate
wartości, więc nadal może być wymagana dodatkowa obróbka, aby to sprawdzić.źródło
Wymaga to serii podkwerend do uszeregowania wartości, ograniczenia ich, a następnie wykonania sumy podczas grupowania
źródło
Spróbuj tego:
źródło
Podzapytanie jest prawie identyczne z zapytaniem. Tylko zmiana się dodaje
źródło
ROW_NUMBER()
).row_number()
jest dostępny .Zbuduj wirtualne kolumny (jak RowID w Oracle)
stół:
dane:
SQL w ten sposób:
jeśli usuniesz klauzulę where w t3, będzie to wyglądać następująco:
UZYSKAJ „TOP N Record” -> dodaj „rownum <= 3” w klauzuli where (klauzula where t3);
WYBIERZ „rok” -> dodaj „MIĘDZY 2000 I 2009” w klauzuli where (klauzula where t3);
źródło
Zajęło mi to trochę pracy, ale myślę, że moim rozwiązaniem byłoby udostępnienie, ponieważ wydaje się eleganckie i dość szybkie.
Zauważ, że ten przykład został podany na potrzeby pytania i można go łatwo modyfikować do innych podobnych celów.
źródło
Następujący post: sql: wybieranie rekordu N na grupę opisuje skomplikowany sposób osiągnięcia tego bez podkwerend.
Poprawia inne oferowane tutaj rozwiązania:
To jednak nie jest ładne. Dobrym rozwiązaniem byłoby osiągnięcie funkcji okna (aka funkcji analitycznych) włączonych w MySQL - ale tak nie jest. Trik zastosowany we wspomnianym poście wykorzystuje GROUP_CONCAT, który jest czasami opisywany jako „Funkcje okna biednego człowieka dla MySQL”.
źródło
dla takich jak ja, którzy mieli czas na zapytania. Zrobiłem poniżej, aby użyć limitów i czegokolwiek innego przez określoną grupę.
zapętla listę domen, a następnie wstawia limit tylko 200
źródło
Spróbuj tego:
źródło
Spróbuj poniżej procedury składowanej. Już zweryfikowałem. Otrzymuję właściwy wynik, ale bez użycia
groupby
.źródło