Vrlo često imamo situaciju da želimo sortiranje po nekom određenom polju u tebeli ali tako da se NULL polja nikada ne pojavljuju na početku. Tipičan primer je recimo frontend koji prikazuje podatke iz neke tabele, gde se klikom na header kolone obavlja sortiranje po rastućem (asc) ili opadajućem (desc) poretku.
Obzirom da se ja u poslednje vreme dosta bavim domenima, kreirao sam jednu tabelu sa par svojih domena, čisto kao demonstraciju koncepta.
mysql> select * from domains; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 1 | dinke.net | 2010-01-17 | | 2 | lampix.net | 2009-12-26 | | 3 | blogodak.com | 2010-09-08 | | 4 | maestrodesert.com | 2009-09-11 | | 5 | nepostojeci.com | NULL | +----+-------------------+-------------+ 5 rows in set (0.00 sec)
Dakle problem, želim sortiranje po expire_date polju ali tako da se NULL polje (recimo domen koji još nije regovan ili je istekao) uvek pojavljuje na kraju. Po defaultu NULL se javlja na početku ako sortiramo u rastućem (ASC) orderu odnosno na kraju ako sortiramo po opadajućem (desc) orderu.
mysql> select * from domains order by expire_date asc; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 5 | nepostojeci.com | NULL | | 4 | maestrodesert.com | 2009-09-11 | | 2 | lampix.net | 2009-12-26 | | 1 | dinke.net | 2010-01-17 | | 3 | blogodak.com | 2010-09-08 | +----+-------------------+-------------+ 5 rows in set (0.00 sec) mysql> select * from domains order by expire_date desc; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 3 | blogodak.com | 2010-09-08 | | 1 | dinke.net | 2010-01-17 | | 2 | lampix.net | 2009-12-26 | | 4 | maestrodesert.com | 2009-09-11 | | 5 | nepostojeci.com | NULL | +----+-------------------+-------------+ 5 rows in set (0.00 sec)
Problem sortiranja ćemo rešiti korišćenjem MySQL-ove IF f-je, a rešenje je:
mysql> select * from domains order by if(expire_date is null, 1, 0), expire_date asc; +----+-------------------+-------------+ | id | domain | expire_date | +----+-------------------+-------------+ | 4 | maestrodesert.com | 2009-09-11 | | 2 | lampix.net | 2009-12-26 | | 1 | dinke.net | 2010-01-17 | | 3 | blogodak.com | 2010-09-08 | | 5 | nepostojeci.com | NULL | +----+-------------------+-------------+ 5 rows in set (0.00 sec)
MySQL-ova IF f-ja slična je ternarnom operatoru, tj. vraća prvi argument ako je iskaz tačan odnosno drugi u slučaju da nije, dakle u ovom slučaju vraća 1 za null vrednosti odnosno 0 za ostale, čime dobijamo upravo prikaz koji želimo tj. NULL polje na kraju liste.
Znam da ovo može delovati pomalo konfuzno pa ću otići još jedan korak dalje i dodati još jedno polje u našoj tabeli čisto radi razjašnjenja šta se ovde tačno događa:
mysql> alter table domains add column nullorder tinyint not null; Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0
a zatim i update-ovati vrednosti nullorder polja tako da sadrže vrednost IF iskaza odozgo:
mysql> update domains set nullorder = if(expire_date is null, 1, 0); Query OK, 1 row affected (0.00 sec) Rows matched: 5 Changed: 1 Warnings: 0 mysql> select * from domains; +----+-------------------+-------------+-----------+ | id | domain | expire_date | nullorder | +----+-------------------+-------------+-----------+ | 1 | dinke.net | 2010-01-17 | 0 | | 2 | lampix.net | 2009-12-26 | 0 | | 3 | blogodak.com | 2010-09-08 | 0 | | 4 | maestrodesert.com | 2009-09-11 | 0 | | 5 | nepostojeci.com | NULL | 1 | +----+-------------------+-------------+-----------+ 5 rows in set (0.00 sec)
Sve u svemu naš gornji query iz rešenja problema:
select * from domains order by if(expire_date is null, 1, 0), expire_date asc;
Potpuno je isto što i ovaj query:
mysql> select * from domains order by nullorder, expire_date; +----+-------------------+-------------+-----------+ | id | domain | expire_date | nullorder | +----+-------------------+-------------+-----------+ | 4 | maestrodesert.com | 2009-09-11 | 0 | | 2 | lampix.net | 2009-12-26 | 0 | | 1 | dinke.net | 2010-01-17 | 0 | | 3 | blogodak.com | 2010-09-08 | 0 | | 5 | nepostojeci.com | NULL | 1 | +----+-------------------+-------------+-----------+ 5 rows in set (0.00 sec)
osim što naravno nullorder polje nismo morali da kreiramo.
Naravno na sličan način možemo dobiti NULL polja na početku u desc prikazu (za slučaj da je to ikome potrebno).