23 Eylül 2008 Salı

SQL Server 2005 de Sıralama (Rank) ve Sıra numarası verme işlemleri

Diyelim ki sitenizde bazı makaleler yayımlıyor ve en çok okunan makaleleri görmek istiyorsunuz..
Bunu basit bir sql cümlesi ile nasıl yaparız?
Birkaç yöntemi olmakla birlikte SQL SERVER 2005 ile gelen Rank() ve Dense_Rank() fonksiyonlarından bahsetmek istiyorum..


Sıralama (Ranking) Fonksiyonları (Rank(),Dense_Rank(),Row_Number() ):
[ranking_func_name] OVER ([partition_by_clause] order_by_clause)


1. ROW_NUMBER() fonksiyonu:
Her satır için 1 den başlayarak ardışık numara üretir. Her satır için farklı sayı ürettiğinden aynı gruplama içerisinde bir sayı sadece bir kez geçer.

Örnek Yazım şekli:

SELECT
ROW_NUMBER() Over( ORDER BY MakaleAdi ) as Row_No_Tumu,
ROW_NUMBER() Over( Partition BY Tur ORDER BY MakaleAdi ) as Row_No_TureGore
FROM @Okunma

2. RANK() fonksiyonu:
Verilen sıralamaya göre aynı değere sahip satırlar, aynı sıra numarasını alırlar. En sonuncu sıra numarası, kayıt sayısına eşittir.
Örneğin "Bir yarışmaya 100 kişi katılır, 100 kişiden 99 u aynı puanı alırsa, 99 kişi 1. olurken, diğer kişi 100. (sonuncu) olur." şeklindeki bir sıralamayı hesaplamak için kullanılır.

Örnek yazım şekli:
SELECT
Rank() Over(PARTITION BY NULL
ORDER BY
OkunmaSayisi DESC) as Siralama_Tumu,
Rank() Over(PARTITION BY Tur
ORDER BY OkunmaSayisi DESC) as Siralama_TureGore
FROM @Okunma


3. DENSE_RANK() fonksiyonu:
Rank fonksiyonu gibi çalışır ancak üretilen rakamlar arasında boşluk olmaz.
Örneğin "Bir yarışmaya 100 kişi katılır, 100 kişiden 99 u aynı puanı alırsa, 99 kişi 1. olurken, diğer kişi 2. olur."
şeklindeki bir sıralamayı hesaplamak için kullanılır.

Örnek yazım şekli:

SELECT
DENSE_RANK() Over(PARTITION BY NULL
ORDER BY
OkunmaSayisi DESC) as Siralama_Tumu,
DENSE_RANK() Over(PARTITION BY Tur
ORDER BY OkunmaSayisi DESC) as Siralama_TureGore
FROM @Okunma


partition_by_clause
Fonksiyonun uygulandığı listeyi gruplara bölmek için kullanılır. Kullanılan sıralama fonksiyonu partition by da yazılan her gruba ayrı ayrı uygulanır ve grup değişiminde fonksiyon hesaplamaya en baştan başlar, sadece bu grup için yapar. Partition by ifadesi yazılmazsa veya "partition by null" yazılırsa tüm kayıt seti için fonksiyon çalışır.

order_by_clause
Sıralama fonksiyonlarının hangi sıraya göre hesaplanacağını burada ifade ederiz.

Aşağıdaki örnek de,makale adına göre bir sıra numarası vermek istersek

SELECT Tur,MakaleAdi,OkunmaSayisi,
ROW_NUMBER() Over( Partition BY NULL ORDER BY MakaleAdi ) as Row_No_Tumu,
ROW_NUMBER() Over( Partition BY Tur ORDER BY MakaleAdi ) as Row_No_TureGore
FROM @Okunma



Aşağıdaki tüm sql cümlesini kopyalayıp çalıştırırsanız, hafızade bir tablo yaratıp içerisine örnek veriler ekleyecek ve
istediğimiz sorguları çalıştıracaktır



-- SQL Script begins

DECLARE @Okunma TABLE
(MakaleID int, MakaleAdi varchar(100), Tur varchar(10),OkunmaSayisi int)

-- Örnek verileri ekleyelim
INSERT INTO @Okunma(MakaleID,OkunmaSayisi,Tur)
SELECT 1,10,'SQL'
UNION SELECT 2,10,'ASP'
UNION SELECT 3,15,'SQL'
UNION SELECT 4,10,'ASP'
UNION SELECT 5,30,'ASP'
UNION SELECT 6,18,'ASP'
UNION SELECT 7,10,'SQL'
UNION SELECT 8,30,'SQL'
UNION SELECT 9,20,'SQL'
UNION SELECT 10,20,'ASP'
UNION SELECT 11,9,'ASP'

-- Makale isimlerini oluşturalım...
UPDATE @Okunma Set MakaleAdi = 'Makale_' + CAST(MakaleID as varchar)


-- Tüm makelelerin listesi
SELECT * FROM @Okunma ORDER BY Tur,MakaleID


SELECT Tur,MakaleAdi,OkunmaSayisi,
Rank() Over(PARTITION BY NULL
ORDER BY
OkunmaSayisi DESC) as Siralama_Tumu,
Rank() Over(PARTITION BY Tur
ORDER BY OkunmaSayisi DESC) as Siralama_TureGore,
Dense_Rank() Over(PARTITION BY NULL
ORDER BY
OkunmaSayisi DESC) as Siralama_Tumu_2,
Dense_Rank() Over(PARTITION BY Tur
ORDER BY OkunmaSayisi DESC) as Siralama_TureGore_2
FROM @Okunma
ORDER BY Tur, Siralama_TureGore


SELECT Tur,MakaleAdi,OkunmaSayisi,
ROW_NUMBER() Over( Partition BY NULL ORDER BY OkunmaSayisi DESC,Tur ) as Row_Okunma

FROM @Okunma

-- SQL Script end


Bu sql cümlelerini çalıştırdığınızda 3 farklı resultset göreceksiniz.

1.@Okunma tablosuna eklediğimiz tüm kayıtlar :

MakaleIDMakaleAdiTurOkunmaSayisi
2Makale_2ASP10
4Makale_4ASP10
5Makale_5ASP30
6Makale_6ASP18
10Makale_10ASP20
11Makale_11ASP9
1Makale_1SQL10
3Makale_3SQL15
7Makale_7SQL10
8Makale_8SQL30
9Makale_9SQL20

2. Okunma sayısına göre makalelerin sıralanması :
TurMakaleAdiOkunmaSayisiSiralama
Tumu
Siralama
TureGore
Siralama
Tumu_2
Siralama
TureGore_2
ASPMakale_5301111
ASPMakale_10203222
ASPMakale_6185333
ASPMakale_4107454
ASPMakale_2107454
ASPMakale_11911665
SQLMakale_8301111
SQLMakale_9203222
SQLMakale_3156343
SQLMakale_7107454
SQLMakale_1107454

Siralama Tumu kolonu : "Rank() Over(PARTITION BY NULL ORDER BY OkunmaSayisi DESC) as Siralama_Tumu" kodu ile oluşturuldu.
Makale_5 ve Makale_8 isimli makaleler 30 hit alarak en çok okunan 1. makale olmayı başarmışlar.
Rank ifadesini biraz daha açalım..
Partition by null ile, sayıları tekrar başlatmak istediğimiz herhangi bir kolon olmadığını, ilgilendiğimiz tüm kayıtlar için sıralama yapılmasını istediğimizi ifade ediyoruz.
Sıralamanın neye göre yapılacağı bilgisi ise "ORDER BY" ifadesinden sonra yazdığımız alan isimleri ile belirtiliyor. Biz "OkunmaSayisi" alanındaki verileri dikkate alacağız ancak en çok okunan makalenin daha üste gelmesini istediğimiz için sıralamanın büyükten küçüğe yapılmasını istediğimizi DESC ifadesini de ekleyerek söylüyoruz.. Buradaki "ORDER BY" ifadesinin yazımı ve anlamı select cümlesinde kullandığımız order by ile aynı... Birden fazla alana göre sıralama yapmak istersek, kolon isimlerini "," ile ayırarak yan yana yazabiliriz..

3. Satır sayısı:
TurMakaleAdiOkunmaSayisiRow_Okunma
ASPMakale_5301
SQLMakale_8302
ASPMakale_10203
SQLMakale_9204
ASPMakale_6185
SQLMakale_3156
ASPMakale_4107
ASPMakale_2108
SQLMakale_1109
SQLMakale_71010
ASPMakale_11911


ROW_NUMBER() fonksiyonu:

Aşağıdaki örnek de, ilk 8 makale için, makale adına göre bir sıra numarası vermek istiyoruz.. İlk kolonda (Row_No_Tumu) de 1 den 8 kadar makale adına göre sıralı numara üretmesi için "partition by null" yazıyoruz. Row_No_TureGore kolonunda ise, her 2 tür içerisinde yine makale adına göre sıralı bir numara üretmesini istiyoruz. ancak her tür için numarayı 1 den başlatmasını da istiyoruz..

SELECT Tur,MakaleAdi,OkunmaSayisi,
ROW_NUMBER() Over( Partition BY NULL ORDER BY MakaleAdi ) as Row_No_Tumu,
ROW_NUMBER() Over( Partition BY Tur ORDER BY MakaleAdi ) as Row_No_TureGore
FROM @Okunma


Örnek sonuç :


TurMakaleAdiOkunma
Sayisi
Row_No TumuRow_No
TureGore
SQLMakale_11011
ASPMakale_21021
SQLMakale_31532
ASPMakale_41042
ASPMakale_53053
ASPMakale_61864
SQLMakale_71073
SQLMakale_83084

Makale_2 isimli makale tüm sıra numarasında 2 numarayı alırken, türe göre de 1 numarasını alıyor. Makale_2 den önce gelen Makale_1 başka bir türde yazılmış bir makale..

Hiç yorum yok: