Siberkuvvet i geliştirken oldukça eğlenci kod geliştirme durumları karşılaşıyorum. Bunlardan birisi de SQL sorguları. Her ne kadar SQL durumlarının birden fazla yazma yöntemi olsa da mümkün olduğu kadar optimize edilmiş ve tek sorguda (SQL de yapabilecekleri SQL de yapın gibi bir optimizasyon kuruları vardı yanlış hatırlamıyorsam) sorguları oluşturmaya çalışıyorum.
Bugün senaryo şu şekilde :
Veritabanında üye listesi mevcut. Her üyenin id, kullanıcı adı, puan ve son puana ulaşma tarihi bilgileri tek bir satır içerisinde bulunuyor. İstediğimiz sorgu de bir kullanıcının puanına göre kaçıncı sırada olduğu. Eğer puanlar aynı ise son puana ulaşma tarihi kimin önce ise o öne geçecek.
Veritabanı :
Tablo : kullanici_puan
id |
kadi |
puan |
puan_tarihi |
1 |
K1 |
32 |
1540368581 |
2 |
K2 |
34 |
1540368584 |
3 |
K3 |
30 |
1540368589 |
4 |
K4 |
37 |
1540368583 |
5 |
K5 |
32 |
1540368582 |
Bu tabloda bir en iyiler sıralaması yapması basit. İki farklı order by ile (puan,puan tarihi) oldukça kolay bir şekilde yapılabilir. Biz tek bir sorguda K5 kullanıcısının kaçıncı sırada olduğunu öğrenmek istiyoruz.
Bazı veritabanlarında bunun için hazır fonksiyonlar var. Bunları kullanmadan genel bir sorgu yazacağız.
Öncelikle id verisini çeken basit sorguyu ekleyelim.
select id from kullanici_puan where id=4;
id |
4 |
Şimdi sıralamasını alalım, bunun için içe içe select sorgusu yapacağız. İçerdeki select sorgusu kullanıcının kendi puanının, tüm puanlardan kaç tanesinden daha küçük olduğu sayısı seçecektir.
select id, (select count(id) from kullanici_puan as p1 where p1.puan > p2.puan ) as position
from kullanici_puan as p2
where id = 4;
id |
position |
4 |
0 |
ID değeri 4 olan kullanıcı en yüksek puana sahip bu nedenle kendisinin küçük olduğu başka puan sayısı 0 geldi. Ama biz sıralama istiyoruz bu nedenle birinci olarak seçilmesi lazım.
select id, (select count(id)+1 from kullanici_puan as p1 where p1.puan > p2.puan ) as position,
from kullanici_puan as p2
where id = 4;
id |
position |
4 |
1 |
Puanları aynı olan K1 ve K5 kullanıcıları için bu sorgu nasıl çalışacaktır ?
select id, (select count(id)+1 from kullanici_puan as p1 where p1.puan > p2.puan ) as position,
from kullanici_puan as p2
where id = 1;
id |
position |
1 |
3 |
select id, (select count(id)+1 from kullanici_puan as p1 where p1.puan > p2.puan ) as position,
from kullanici_puan as p2
where id = 5;
id |
position |
5 |
3 |
Gördüğünüz gibi sıralama aynı geldi, çünkü puan_tarihi kısmını devreye katmadık. Halbuki K1 puana daha önce ulaştığı için 3. o olmalı. K5 ise 4. sırada olmaldı.
Sırada aynı puana sahip olanları sıralama işlemi kaldı. Bunun için de iç select sorgusunda bir koşul ekleyeceğiz. Diyeceğiz ki kullanıcının kendi puanının, ( diğer puanlardan daha küçük olduğu sayı veya (kullanıcını puanı diğer puanlara eşit ve puan tarihi diğerlerinden daha küçük) olan durumların sayısını seçecektir.
select id, (select count(id) from kullanici_puan as p1 where p1.puan > p2.puan or (p1.puan = p2.puan and p1.puan_tarihi < p2.puan_tarihi)) as position
from kullanici_puan as p2
where id = 1;
id |
position |
1 |
3 |
where id=5
id |
position |
5 |
4 |
İşlem tamamdır.
Herkese iyi çalışmalar.