This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!
Język SQL kryje w sobie wiele różnych tajemnic, których znajomość może bardzo ułatwić życie programisty. Nawet podczas korzystania z jego zwykłych - “nieprogramowalnych” elementów, takich jak podstawowe zapytania CRUD [mówiąc “programowalne”, mam na myśli wszelkiego rodzaju procedury składowane i im podobne] możemy dowiedzieć się o wielu sztuczkach, które potrafią przeobrazić zwracane wyniki w gotowy zestaw danych gotowy do użycia. Mając w pamięci jeden z moich niedawnych wpisów o zwracaniu numeru rekordu, dzisiaj także chciałbym przedstawić Wam jedną z takich sztuczek.
Wstęp. Niecałe dwa tygodnie temu stanąłem przed niemałą zagwozdką - pobierałem z tabel rekordy, których dane miały tworzyć listę w odpowiedniej kolejności. Uprzedzam pytania - w zbiorze wynikowym nie było żadnego pola “ordering”, według którego można by uszeregować te dane, a wprowadzenie go i oprogramowanie w panelu administracyjnym nie było aż tak istotne, żeby się tym zająć na poważnie. Potrzebne było rozwiązanie “na szybko”, które jednak nie powodowałoby istotnego wzrostu współczynnika WTF/min dla ewentualnych przyszłych programistów, którzy zajrzeliby do tego projektu:
Obrazek: http://www.osnews.com/story/19266/WTFs_m.
Nie mogłem zatem skorzystać ze standardowej klauzuli ORDER BY [żadne pole nie dawałoby dobrych wyników], więc musiałem znaleźć coś, co pozwoli na posortowanie rekordów według własnej, ustalonej kolejności. Tradycyjnie zajrzałem do Internetu w poszukiwaniu czegoś, co rozwiązałoby mój problem.
Rozwiązanie.
Po krótkich poszukiwaniach mogę powiedzieć jedno - po raz kolejny nie zawiodłem się na MySQLu. Mówcie sobie co chcecie - ten silnik bazodanowy jest po prostu dobry, a żeby było śmieszniej, takiego rozwiązania nie posiada np. konkurencyjny, a także darmowy PostgreSQL. W każdym razie nie chcę wywoływać kolejnej wojny religijnej, więc skupmy się na samym rozwiązaniu. ;]Rozwiązanie jest bardzo proste - MySQL udostępnia nam w tym przypadku ciekawą funkcję FIELD(), która pozwala na zdefiniowanie własnej listy wartości branych pod uwagę podczas sortowania wierszy. Jego składnia brzmi następująco:
|
|
gdzie [field] to nazwa pola, którego wartości będą porównywane, a [value1], [value2], [value3] i tak dalej, to kolejne wartości sortujące. Jeśli to nie byłoby jasne, pozwolę sobie pomóc przykładem: ;]
Weźmy pod uwagę tabelę:
|
|
i przykładowe dane w tej tabeli:
|
|
Wykonując zapytanie pobierające wszystkie rekordy:
|
|
dostajemy poniższą listę:
Załóżmy, że chcielibyśmy jednak posortować te pozycje według ich faktycznej kolejności - tak, jak wcześniej wspomniałem możemy oczywiście skorzystać z pola ordering, które wypełnimy odpowiednimi numerkami, jednak to rozwiązanie nie pasuje do tematyki tego wpisu. ;] Zamiast niego możemy po prostu wykonać:
|
|
Lub też:
|
|
I zwrócony wynik jest już całkiem poprawny:
Ważne: w przypadku, kiedy w funkcji FIELD() wyspecyfikujemy tylko część wartości pojawiających się w wierszach tabeli [np. kolejny rekord 5 => “Fifth”], to wszystkie inne zostaną posortowane według domyślnych reguł silnika bazodanowego, czyli po prostu będą wyrzucone do wyniku w mniej lub bardziej losowy sposób. ;]
Jak można zauważyć, do funkcji FIELD() można także zastosować modyfikatory ASC / DESC, stąd wniosek, że funkcję tą można zastosować jako normalny element listy w klauzuli ORDER BY. Polecam wykorzystywanie takich sztuczek. ;]