W jaki sposób ORDER BY FIELD () w MySQL działa wewnętrznie

37

Rozumiem, jak ORDER BYdziała klauzula i jak FIELD()działa ta funkcja. Chcę zrozumieć, jak oboje pracują razem przy sortowaniu. W jaki sposób pobierane są wiersze i jak wyliczana jest kolejność sortowania

+----+---------+
| id |  name   |
+----+---------+
|  1 | stan    |
|  2 | kyle    |
|  3 | kenny   |
|  4 | cartman |
+----+---------+ 

SELECT * FROM mytable WHERE id IN (3,2,1,4) ORDER BY FIELD(id,3,2,1,4)

Powyższe zapytanie spowoduje

+----+---------+
| id |  name   |
+----+---------+
|  3 | kenny   |
|  2 | kyle    |
|  1 | stan    |
|  4 | cartman |
+----+---------+ 

coś podobnego do powiedzenia ORDER BY 3, 2, 1, 4

PYTANIA

  • Jak to działa wewnętrznie?
  • W jaki sposób MySQL pobiera wiersze i oblicza porządek sortowania?
  • Skąd MySQL wie, że musi sortować według kolumny id?
itz_nsn
źródło
1
wypróbuj następującą odmianę zapytania: SELECT *, FIELD(id,3,2,1,4) AS f FROM mytable WHERE id IN (3,2,1,4);następnie dodaj ORDER BY flub ORDER BY FIELD(id,3,2,1,4)spróbuj ponownie.
ypercubeᵀᴹ

Odpowiedzi:

64

Dla przypomnienia

SELECT * FROM mytable WHERE id IN (1,2,3,4) ORDER BY FIELD(id,3,2,1,4);

powinien również działać, ponieważ nie musisz zamawiać listy w WHEREklauzuli

Jeśli chodzi o to, jak to działa,

  • FIELD () to funkcja, która zwraca pozycję indeksu listy rozdzielanej przecinkami, jeśli szukana wartość istnieje.

  • Te ORDER BYwartości są oceniane przez co polu () powraca

Możesz tworzyć różnego rodzaju fantazyjne zamówienia

Na przykład, stosując IF () funkcji

SELECT * FROM mytable
WHERE id IN (1,2,3,4)
ORDER BY IF(FIELD(id,3,2,1,4)=0,1,0),FIELD(id,3,2,1,4);

Spowoduje to, że pierwsze 4 identyfikatory pojawią się na górze listy, w przeciwnym razie pojawi się na dole. Czemu?

W ORDER BYalbo dostajesz 0 albo 1.

  • Jeśli pierwsza kolumna ma wartość 0, wyświetl dowolne z pierwszych 4 identyfikatorów
  • Jeśli pierwsza kolumna to 1, spraw, by pojawiła się później

Odwróćmy to za pomocą DESC w pierwszej kolumnie

SELECT * FROM mytable
WHERE id IN (1,2,3,4)
ORDER BY IF(FIELD(id,3,2,1,4)=0,1,0) DESC,FIELD(id,3,2,1,4);

W ORDER BYdalszym ciągu dostajesz 0 lub 1.

  • Jeśli pierwsza kolumna to 1, zrób cokolwiek oprócz pierwszych 4 identyfikatorów.
  • Jeśli pierwsza kolumna ma wartość 0, spraw, aby pierwsze 4 identyfikatory pojawiały się w oryginalnej kolejności

TWOJA RZECZYWISTA PYTANIE

Jeśli poważnie chcesz mieć na to elementy wewnętrzne, przejdź do stron 189 i 192 Księgi

MySQL Internals

na głębokie nurkowanie.

Zasadniczo istnieje klasa C ++ o nazwie ORDER *order( ORDER BYDrzewo wyrażeń). W JOIN::prepare, *orderjest używana w funkcji o nazwie setup_order(). Dlaczego w środku JOINklasy? Każde zapytanie, nawet zapytanie dotyczące pojedynczej tabeli, jest zawsze przetwarzane jako JOIN (Zobacz mój post Czy istnieje różnica w wykonywaniu między warunkiem JOIN a warunkiem WHERE? )

Kod źródłowy tego wszystkiego to sql/sql_select.cc

Najwyraźniej ORDER BYdrzewo będzie miało ocenę FIELD(id,3,2,1,4). Tak więc liczby 0,1,2,3,4 są wartościami sortowanymi z odniesieniem do danego wiersza.

RolandoMySQLDBA
źródło
1
To jest wyjątkowo doskonałe wytłumaczenie. Korzystając z tych metod, udało mi się uzyskać 3 zamówienia, pierwotną pierwszą wartość, która jest maksimum zestawu, następnie według FIELD, a następnie innej kolumny dla tych, których nie ma w zestawie FIELD. Coś, o czym nie śniłem kiedyś. Dziękujemy za poświęcenie czasu na wyjaśnienie, jak to naprawdę działa.
Lizardx
Załóżmy, że istnieją Nwartości zarówno w, jak INi FIELD. W tym przykładzie N=4. Czy rozumiem poprawnie, że to zapytanie wykona co najmniej ~N^2operacje. Ponieważ każde FIELDobliczenie dokonuje ~Nporównań raz dla każdego wiersza. Jeśli tak, jest to dość powolne jak na duże NMoże to nie jest bardzo dobre podejście?
Gherman
@Gherman FIELD()Funkcja powinna być O(1)operacją, ponieważ FIELD()ma indeks liczbowy id. Więc nie widzę nic innego, jak O(n)na podstawie wierszy. Nie widzę FIELD()wykonywania operacji iteracyjnych, które GREATEST()byłyby konieczne.
RolandoMySQLDBA
@RolandoMySQLDBA Chodzi mi o to, że jeśli FIELDma Nargumenty do porównania, wykona Nporównania. Jak inaczej będzie porównywać jedną liczbę z Ninnymi liczbami, jeśli nie robiąc tego O(N)? Jedyną możliwością, jaką mogę wymyślić, jest jakaś optymalizacja poprzez specjalną strukturę danych, taką jak skrót lub drzewo argumentów. Właściwie wiem, że INma taką optymalizację. Nie wiem, o FIELD. Co rozumiesz przez „indeks liczbowy”?
Gherman
1
Hej @RaymondNijland, instrukcja CASE jest bardziej zrozumiała. W tym przypadku cukier składniowy ma po prostu mniej pisania.
RolandoMySQLDBA
1

Być może będzie to zbyt daleko od rzeczywistego kodu, więc nie będzie wystarczająco niskiego poziomu od tego, co chciałeś:

Gdy MySQL nie może użyć indeksu do pobierania danych w posortowanej kolejności, tworzy tymczasową tabelę / zestaw wyników ze wszystkimi wybranymi kolumnami i pewnymi dodatkowymi danymi - jedna z nich jest pewnego rodzaju kolumną do przechowywania wyników wartości wyrażenia ORDER BY dla każdego wiersza - następnie wysyła tę tabelę tmp do rutine „fileort” z informacją, według której kolumny posortować. Następnie wiersze są posortowane, dzięki czemu można je wybierać jeden po drugim i zwracać wybrane kolumny.

jkavalik
źródło
To objaśnienie nie bierze pod uwagę sposobu FIELDobliczania funkcji. Obawiam się, że może to mieć znaczący wpływ na wydajność.
Gherman
@Gherman Nie sądzę, chyba że używasz bardzo długiej listy argumentów (ponieważ funkcja jest liniowa względem liczby argumentów . Dostęp do danych jest o rząd wielkości wolniejszy niż proste porównania.
jkavalik
Tak, długa lista argumentów. W tym przykładzie jest tyle argumentów, ile jest rekordów.
Gherman
Oznaczę
dlaczego nie setki wyników? Czy to dużo?
Gherman