Czy czasem wyobrażasz siebie drapiącego się w głowę kiedy w Twoich myślach kłębi się pytanie: czy dałoby się prościej zapisać to zapytanie? Rzeczywiście minę można mieć nietęgą, adekwatną do problemu. Aby nie wpadać więcej na taką minę pokażę Ci dziś kilka prostych porad.
IF "binarny".
Czyli prosty IF gdzie na podstawie warunku chcemy zwrócić 1 lub 0. Standardowo narzuca się więc postać typu:
SELECT IF(pole=4,1,0) ...
W przypadku gdy pole ma wartość 4, zwrócimy 1, w przeciwnym wypadku zwrócimy 0. A co zwraca wyrażenie a=4? Zwraca TRUE lub FALSE, które w wynikach zapytań reprezentowane są jako 1 i 0.
Czyli prościej będzie:
SELECT pole=4 ...
IF "niebinarny".
W tym przypadku w zależności od warunku zwracamy dwie różne liczby i w przeciwieństwie do poprzedniego przykładu, będą to inne liczby niż 0 i 1.
SELECT IF(pole=3,4,2) ...
Ponieważ wyrażenie wewnątrz IF, jak już wiemy, zwraca 0 lub 1, musimy sprowadzić ten wynik do 2 lub 4.
Możemy to zrobić tak:
SELECT ((pole=3)*2+2) ...
/* lub */
SELECT (((pole=3)+1)*2) ...
I jeszcze kilka przykładów:
SELECT IF(pole<5,10,0) ...
SELECT (pole<5)*10 ...
SELECT IF(pole>2,-1,0) ...
SELECT -(pole>2) ...
SELECT IF(pole>2,0,1) ...
SELECT NOT pole>2 ... /* lub */ SELECT pole<=2 ...
Nic nie stoi na przeszkodzie, aby powyższych uproszczeń używać w funkcjach grupujących, np. SUM:
SELECT SUM(IF(pole=4,1,0)) ...
SELECT SUM(pole=4) ...
Należy jednak pamiętać, że w tym przypadku suma jest sumą jedynek tylko kiedy pole rekordu ma wartość 4, a nie jest sumą czwórek dla rekordów spełniających ten warunek. W przypadku użycia standardowego IF jest to czytelniejsze. Jeszcze inne pasujące jak ulał miejsce dla takiego uproszczonego IF, to klauzula WHERE. Wtedy akurat:
WHERE a=2
jest czytelniejsze niż:
WHERE IF(a=2,1,0)
W(y)rażenia z piekła rodem.
Przykład realny. Mamy dwie kolumny: czas rozpoczęcia zadania oraz czas skończenia zadania o typie DATETIME. Użytkownik przy rozwiązywaniu zadania widzi czas odliczany w dół, od 60 sekund. Jeśli zadanie rozwiąże w czasie 0 sekund, to otrzymuje 60 punktów, w czasie 10 sekund, 50 punktów, w czasie 50 sekund, 10 punktów. Jeśli przekroczy czas 60 sekund nie naliczamy punktów.
Załóżmy:
T - czas na zadanie = maksymalna liczba punktów,
dt - czas wykonania zadania
wtedy:
punkty = T - dt (punkty >=0)
Zapiszmy to w MySQL:
SELECT 60-(UNIX_TIMESTAMP(koniec_pytania)-UNIX_TIMESTAMP(start_pytania)) ...
Ale zaraz, nie uwzględniliśmy tu sytuacji gdy czas wykonywania zadania jest większy niż 60 sekund i otrzymamy wtedy wartość mniejszą niż 0 (nie możemy przyznać mniej niż 0 punktów). No dobra, więc potrzebny IF:
SELECT 60-IF(UNIX_TIMESTAMP(koniec_pytania)-UNIX_TIMESTAMP(start_pytania)>60,60,(UNIX_TIMESTAMP(koniec_pytania)-UNIX_TIMESTAMP(start_pytania))) ...
Ekstra, co? Dobrze, że wyrażenie nie jest dłuższe. Całe szczęście, że w tym przypadku przychodzi nam z pomocą mało znana funkcja LEAST (ma też swojego przeciwnika GREATEST). Funkcja LEAST zwraca najmniejszą wartość ze zbioru liczb, ale w przeciwieństwie do funkcji grupującej SUM, ta działa na zbiorze wartości podanych jako kolejne argumenty - nie ma związku z klauzulą GROUP BY.
Jeśli więc chcemy wyrażenie ograniczyć do 60, to nasze zapytanie będzie wyglądać tak:
SELECT 60-LEAST(UNIX_TIMESTAMP(koniec_pytania)-UNIX_TIMESTAMP(start_pytania),60) ...
W przypadku kiedy czas zadania przekroczy 60 sekund, funkcja LEAST wybierze wartość 60, w przeciwnym wypadku wybierze wartość wyrażenia. Od 60 odejmiemy więc maksymalnie 60.
A co, jeśli... jeśli na blogu pojawi się nowy wpis? Zajrzysz?
Przydatne linki:
Funkcja IF w MySQL
Funkcja LEAST w MySQL
Funkcja GREATEST w MySQL