Jak MySQL przetwarza ORDER BY i LIMIT w zapytaniu?

250

Mam zapytanie, które wygląda następująco:

SELECT article FROM table1 ORDER BY publish_date LIMIT 20

Jak działa ORDER BY? Czy zamówi wszystkie rekordy, a następnie zdobędzie pierwsze 20, czy też otrzyma 20 rekordów i zamówi je według publish_datepola?

Jeśli to ostatni, nie masz gwarancji, że naprawdę otrzymasz najnowsze 20 artykułów.

Alex
źródło
6
Zauważ, że jeśli niektóre publish_dates są równe, ich uporządkowanie nie daje określonych wyników, co oznacza, że ​​jeśli użyjesz LIMITpaginacji, możesz dostać te same elementy na różnych stronach!
Konrad Morawski

Odpowiedzi:

244

Najpierw zamówi, a potem dostanie pierwsze 20. Baza danych będzie również przetwarzać wcześniej wszystko w tej WHEREklauzuli ORDER BY.

James
źródło
1
Więc czas jest taki sam?
Yasar Arafath
7
Źle! LIMITprzerwy ORDER BY. Ze LIMITw ORDER BYwyniku niewłaściwego powraca. LIMITw jakiś sposób porządkuje zestaw wyników zwrócony przezORDER BY
Green
6
@Green, mylisz się. Przeczytaj to wyjaśnienie: dev.mysql.com/doc/refman/5.7/en/limit-optimization.html Po zaindeksowaniu kolumny ORDER BY może zwrócić rekordy w innej kolejności niż bez LIMIT, jeśli jest więcej niż 1 rekordy o tej samej wartości w tej kolumnie.
yitwail
1
Jednym szybkim rozwiązaniem takich problemów jest dodanie jeszcze jednej kolumny do zamówienia, najlepiej dzięki unikalnym wartościom, aby baza danych otrzymywała spójną regułę porządkowania wierszy, gdy wartość pierwszego zamówienia według kolumny jest taka sama dla wielu wierszy.
rineez
37

Klauzula LIMIT może służyć do ograniczenia liczby wierszy zwracanych przez instrukcję SELECT. LIMIT przyjmuje jeden lub dwa argumenty liczbowe, które muszą być nieujemnymi stałymi liczbami całkowitymi (z wyjątkiem sytuacji, gdy używane są przygotowane instrukcje).

W przypadku dwóch argumentów pierwszy argument określa przesunięcie pierwszego wiersza do zwrócenia, a drugi określa maksymalną liczbę wierszy do zwrócenia. Przesunięcie początkowego rzędu wynosi 0 (nie 1):

SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15

Aby pobrać wszystkie wiersze od określonego przesunięcia do końca zestawu wyników, możesz użyć dużej liczby dla drugiego parametru. Ta instrukcja pobiera wszystkie wiersze od 96-tego wiersza do ostatniego:

SELECT * FROM tbl LIMIT 95,18446744073709551615;

Za pomocą jednego argumentu wartość określa liczbę wierszy do zwrócenia od początku zestawu wyników:

SELECT * FROM tbl LIMIT 5; # Retrieve first 5 rows

Innymi słowy, LIMIT liczba_wierszy jest równoważna LIMIT 0, liczba_wierszy.

Wszystkie szczegóły na: http://dev.mysql.com/doc/refman/5.0/en/select.html

bensiu
źródło
Czy nie pobiera wierszy 5-14?
Adonis K. Kakoulidis,
@adonis Nie, to nie jest. Przykład pochodzi z dokumentacji MySQL
dcaswell
Liczba 5 to szósty rząd. 5 wierszy (od 0 do 4) jest ignorowanych.
Phil Perry
1
Ale użycie LIMIT bez ORDER BY może dawać niespójne wyniki! Niestety cały zestaw wyników musi zostać zamówiony przed zastosowaniem LIMIT lub DBMS może dowolnie zamówić wynik, a następnie OFFSET i LIMIT w tym zestawie. Czytałem, że może to wynikać z wyboru przez DBMS alternatywnego planu zapytań opartego na OFFSET i LIMIT, a więc na arbitralnym zamówieniu.
Barton
4
pytanie dotyczy limitu i zamówienia według. Ale odpowiedź wcale nie jest związana z tym pytaniem
Shen Liang
9

Tak jak mówi @James, uporządkuje wszystkie rekordy, a następnie zdobędzie pierwsze 20 wierszy.

Ponieważ tak jest, masz gwarancję otrzymania 20 pierwszych opublikowanych artykułów, nowsze nie zostaną pokazane.

W sytuacji, polecam, aby dodać descdo order by publish_date, jeśli chcesz najnowsze artykuły, a następnie najnowszy artykuł będzie pierwszy.

Jeśli chcesz zachować wynik w kolejności rosnącej i nadal chcesz tylko 10 najnowszych artykułów, możesz poprosić mysql o dwukrotne posortowanie wyniku.

To zapytanie poniżej posortuje wynik malejący i ograniczy wynik do 10 (to jest zapytanie w nawiasie). Nadal będzie sortowany w porządku malejącym i nie jesteśmy z tego zadowoleni, dlatego prosimy mysql o posortowanie go jeszcze raz. Teraz mamy najnowszy wynik w ostatnim wierszu.

select t.article 
from 
    (select article, publish_date 
     from table1
     order by publish_date desc limit 10) t 

order by t.publish_date asc;

Jeśli potrzebujesz wszystkich kolumn, robi się to w następujący sposób:

select t.* 
from 
    (select * 
     from table1  
     order by publish_date desc limit 10) t 

order by t.publish_date asc;

Używam tej techniki, gdy ręcznie piszę zapytania, aby sprawdzić bazę danych pod kątem różnych rzeczy. Nie korzystałem z niego w środowisku produkcyjnym, ale teraz, kiedy go zaznaczam, dodatkowe sortowanie nie wpływa na wydajność.

emanciperingsivraren
źródło
2
Twoje dodatkowe sortowanie może praktycznie nie mieć żadnego wymiernego wpływu na wydajność, ponieważ jest ograniczone tylko do 10 wierszy / elementów :-). Ogólnie rzecz biorąc, sortowanie tabeli w pamięci (którą tworzy podselekcja) jest bardzo szybkie i ledwo mierzalne, chyba że masz miliony wierszy lub DBMS stronicuje zestaw wyników na dysk, ponieważ nie pasuje do pamięci (w takim przypadku w zależności od DBMS może również przerwać zapytanie).
Martin Kersten
7

Jeśli istnieje odpowiedni indeks, w tym przypadku na publish_datepolu, to MySQL nie musi skanować całego indeksu, aby uzyskać 20 żądanych rekordów - 20 rekordów znajdzie się na początku indeksu. Ale jeśli nie ma odpowiedniego indeksu, konieczne będzie pełne skanowanie tabeli.

Jest artykuł na blogu MySQL Performance z 2009 roku.

Martin Clayton
źródło
7

Możesz dodać [asc] lub [desc] na końcu zamówienia, aby uzyskać najwcześniejsze lub najnowsze rekordy

Na przykład najpierw otrzymają najnowsze rekordy

ORDER BY stamp DESC

Dodaj LIMITklauzulę poORDER BY

Paweł
źródło
3
Witamy w Stackoverflow. Myślę, że mogłeś źle zrozumieć pytanie. Myślę, że pytali raczej o kolejność operacji niż o „jak sortować”. (Ale jest to dyskusja, ponieważ odpowiedź na to pytanie już dawno temu;)
Leigh
5

Możesz użyć tego kodu, SELECT article FROM table1 ORDER BY publish_date LIMIT 0,10 gdzie 0 jest początkowym limitem rekordu i 10 liczbą rekordów

gaurangkathiriya
źródło
8
Nie, to nie jest wymagane . LIMIT 10jest skrótem od LIMIT 0,10.
Lawrence Dol
2
tak, nie jest wymagane dla LIMIT 0,10 Ale możesz tego wymagać 10,20
gaurangkathiriya
3

LIMIT jest zwykle stosowany jako ostatnia operacja, więc wynik najpierw zostanie posortowany, a następnie ograniczony do 20. W rzeczywistości sortowanie zatrzyma się, gdy tylko zostanie znalezionych pierwszych 20 posortowanych wyników.

Egor Pavlikhin
źródło
11
Twoje drugie zdanie jest sprzeczne z pierwszym. Sortowanie nie może zostać zatrzymane po znalezieniu pierwszych 20 wyników, ponieważ, jak powiedziałeś, sortowanie zostanie wykonane przed zwróceniem wyników. MySQL może wiedzieć tylko, jakie są pierwsze 20 wyników po zakończeniu sortowania.
Tom
@ Tom, faktycznie może, jeśli zamawia się według indeksowanej kolumny. Wyjaśniono to tutaj: dev.mysql.com/doc/refman/5.7/en/limit-optimization.html
yitwail
-3

Również składnia LIMIT jest różna w zależności od baz danych, na przykład:

mysql - LIMIT 1, 2

postgres - LIMIT 2 PRZESUNIĘCIE 1

harish sharma
źródło