SQL Sıralama Fonksiyonları: ROW_NUMBER, RANK ve DENSE_RANK Nasıl Seçilir
SQL'de ROW_NUMBER, RANK ve DENSE_RANK fonksiyonlarının farkını, beraberlik davranışını ve her grupta ilk kaydı seçme örneklerini öğrenin.
Sıralama fonksiyonları ne yapar?
ROW_NUMBER, RANK ve DENSE_RANK, satırlara belirlediğiniz sıraya göre sıra numarası veren pencere fonksiyonlarıdır. Hepsi OVER cümlesiyle çalışır ve satırları gruplayıp birleştirmez; her satır kendi kimliğiyle kalır, yanına bir sıra sütunu eklenir. Aralarındaki tek fark eşit değerlerin (beraberliklerin) nasıl ele alındığıdır.
- ROW_NUMBER: her satıra benzersiz, ardışık bir numara verir. Eşit değerler olsa bile numaralar farklıdır.
- RANK: eşit değerleri aynı sıraya koyar, ardından gelen sırayı eşit satır sayısı kadar öteler.
- DENSE_RANK: eşit değerlere aynı sırayı verir ama sıra numarasında atlama yapmaz.
Örneğin satışçıları tutarlarına göre yüksekten düşüğe sıralayalım ve ortadaki satışçıların tutarı eşit olsun. ROW_NUMBER şu sırayı üretir: 1, 2, 3, 4. RANK şunu üretir: 1, 2, 2, 4. DENSE_RANK ise şunu: 1, 2, 2, 3. RANK'ta eşitlikten sonra gelen sıra numarası atlanır; DENSE_RANK'ta boşluk bırakılmaz.
Hangisinin seçileceği, sorunun cevabına bağlıdır: "Her satıra benzersiz numara lazım" ise ROW_NUMBER, "Spor tablosundaki gibi yarışma sıralaması" ise RANK, "Kaçıncı farklı değer" ise DENSE_RANK.
Söz dizimi: OVER, PARTITION BY ve ORDER BY
Bu fonksiyonların hepsi aynı kalıpla yazılır:
SELECT satisci, bolge, tutar, ROW_NUMBER() OVER (ORDER BY tutar DESC) AS sira_rn, RANK() OVER (ORDER BY tutar DESC) AS sira_rank, DENSE_RANK() OVER (ORDER BY tutar DESC) AS sira_dense FROM satislar;
OVER içindeki ORDER BY, numaranın hangi sıraya göre verileceğini belirler. Bu cümle zorunludur; sıralama fonksiyonları sıra bilgisi olmadan anlamlı çalışmaz. Fonksiyonların parantezi boştur, çünkü PostgreSQL belgesinin belirttiği gibi OVER cümlesi davranışı tamamen belirler.
PARTITION BY eklendiğinde numaralandırma her grupta baştan başlar:
ROW_NUMBER() OVER (PARTITION BY bolge ORDER BY tutar DESC)
Bu satır, her bölgede tutarı en yüksek satışçıya ilk numarayı verir. PARTITION BY gruplama gibi görünür ama GROUP BY'dan farkı satırları tek satıra indirmemesidir. Pencere fonksiyonları yalnızca SELECT listesinde ve ORDER BY'da kullanılabilir; WHERE, GROUP BY ya da HAVING içinde kullanılamaz.
Her grupta ilk kaydı seçmek
Bu fonksiyonların en yaygın kullanımı, her gruptan belirli bir kaydı çekmektir: her müşterinin son siparişi, her bölgenin en yüksek satışı, her ürünün en güncel fiyatı. Pencere fonksiyonu WHERE içinde doğrudan kullanılamadığı için sorgu bir CTE ya da alt sorguya sarılır ve filtre dışarıda yapılır:
WITH sirali AS ( SELECT customer_id, siparis_id, siparis_tarihi, ROW_NUMBER() OVER ( PARTITION BY customer_id ORDER BY siparis_tarihi DESC ) AS sira FROM siparisler ) SELECT customer_id, siparis_id, siparis_tarihi FROM sirali WHERE sira = 1;
Bu sorgu, her müşteri için yalnızca son siparişi getirir. İlk N kayıt gerektiğinde koşul sira <= :n biçimine dönüşür. Burada ROW_NUMBER bilinçli seçilmiştir: her müşteriden tam olarak bir satır istendiğinde, tarihte beraberlik olsa bile benzersiz numara tek satır döndürür. RANK kullanılsaydı eşit tarihli siparişler aynı sırayı alır ve müşteri başına birden fazla satır gelebilirdi.
Eşit değerlerde hangisinin seçileceği önemliyse, ORDER BY'a ikinci bir sütun eklenmelidir: örneğin siparis_tarihi DESC, siparis_id DESC. Belgeye göre ROW_NUMBER'da eşit satırlar belirsiz sırayla numaralanır; yani ikinci bir sıralama sütunu olmadan sonuç çalıştırmadan çalıştırmaya değişebilir.
Beraberlikli sıralama: ne zaman RANK, ne zaman DENSE_RANK
Beraberliklerin anlamlı olduğu raporlarda RANK ya da DENSE_RANK kullanılır. Bir satış yarışmasında aynı tutarı yapan satışçılar aynı sırada görünmelidir; ROW_NUMBER onlardan birini haksız yere öne çıkarır.
RANK ve DENSE_RANK arasındaki seçim, listede "boşluk" isteyip istemediğinize bağlıdır:
- RANK, spor ve yarışma sıralamasına benzer: eşitlik varsa sonraki sıra atlanır. "İlk N" gibi bir kesit alınırsa beraberlik nedeniyle N kişiden fazlası listelenebilir.
- DENSE_RANK, "en yüksek ikinci değer" türü sorular için uygundur: eşit değerler ne kadar olursa olsun, ikinci farklı değer her zaman ikinci sıradaki satırlardır.
"Maaşı en yüksek ikinci çalışan" gibi mülakat sorularının tipik temiz çözümü DENSE_RANK ile bir CTE kurup sıra değerini filtrelemektir. Aynı sonuca alt sorgu ve MAX ile de ulaşılır, ancak DENSE_RANK, N. farklı değere genelleştirmeyi kolaylaştırır.
Hangi fonksiyonun kullanıldığını yanlış anlamak, rapor sonucunu sessizce bozar: RANK yerine ROW_NUMBER kullanan bir sıralama tablosu, eşit tutarlı satışçılara farklı sıra gösterir ve kimse hata olduğunu fark etmeyebilir.
Tekrarlı kayıtları ayıklamak için ROW_NUMBER
ROW_NUMBER'ın ikinci büyük kullanımı yinelenen kayıt tespitidir. Aynı e-posta adresiyle birden fazla kaydın bulunduğu bir müşteri tablosunda, her e-posta grubunu kendi içinde numaralandırır ve ilk kayıt dışındakileri aday yinelenen olarak işaretlersiniz:
ROW_NUMBER() OVER (PARTITION BY eposta ORDER BY olusturma_tarihi) AS sira
Burada sıra değeri ilk sıradan büyük olan satırlar, aynı e-postanın sonradan eklenen kopyalarıdır. Hangi kaydın korunacağı ORDER BY içinde belirlenir: en eski mi, en yeni mi. Silme işleminden önce her zaman SELECT ile aday satırları görün ve işlemi transaction içinde çalıştırın. Ayrıntı için tekrar eden kayıtları bulma ve silme sayfası bu kullanımı kendi başına işler.
Bir başka yaygın örnek, sayfalama ve satır numarası üretmektir; ancak büyük tablolarda tüm satırları numaralayıp sonra filtrelemek maliyetlidir, bu durumda sıralama sütununda indeks ve anahtar tabanlı sayfalama değerlendirilir.
Sık yapılan hatalar
Sıralama fonksiyonlarında karşılaşılan hatalar:
- Fonksiyonu WHERE içinde kullanmaya çalışmak. Çözüm: CTE ya da alt sorguya sarmak.
- ORDER BY yazmadan sıralama beklemek. Pencere içindeki ORDER BY eksikse numaralar anlamsız olur ya da sorgu hata verir.
- Eşit değerleri düşünmeden ROW_NUMBER seçmek ve rapordan haksız sıralama çıkarmak.
- Eşit değerlerde ikinci sıralama sütunu vermeyip tekrarlanamayan sonuç üretmek.
- PARTITION BY ile GROUP BY'ı karıştırmak; ikincisi satırları azaltır, birincisi azaltmaz.
- NULL değerlerin sıralamada nerede çıkacağını kontrol etmemek; veritabanları NULL'ları sıralamanın başında ya da sonunda bırakabilir ve bu davranış ürüne göre farklıdır.
Kısa doğrulama alışkanlığı: sonuçta her grupta beklenen sayıda satır olup olmadığını COUNT ile kontrol edin ve bazı grupları elle inceleyin. En ucuz hata yakalama yolu, bazı satırları gözle okumaktır. Sonraki adımlar için pencere fonksiyonlarına genel bakış sayfası, önceki ve sonraki satırı karşılaştırmak için LAG ve LEAD sayfası uygundur.
Mindset programlarında bu beceri nerede çalışılır?
Sıralama fonksiyonları, rapor ve analiz yapan herkesin ihtiyaç duyduğu bir beceri: satış, müşteri ve ürün verilerinde "her gruptan ilk kayıt", "en yüksek N" ve "tekrarlı kayıt" soruları bunlarla çözülür. Mindset'in SQL ile Veri Sorgulama (İleri) programında Pencere Fonksiyonları ile İleri Analiz ayrı bir konu başlığıdır; SQL ile İleri Veri Dönüşümü ve Modelleme programının ilk modülü de Pencere Fonksiyonları'dır.
Bu programlar SQL'in temelini bilen analist, raporlama uzmanı ve veri mühendisi için uygundur. Kurumsal ekiplerde satış ya da operasyon raporlarını tekrar tekrar elle hazırlayan çalışanlar, kendi tablolarındaki gerçek sorularla çalışabilir. Program kapsamı ayrıntılı olarak eğitim sayfalarında yer alır.
İlgili Mindset programı
SQL ile Veri Sorgulama (İleri)
Sık sorulan sorular
ROW_NUMBER, RANK ve DENSE_RANK arasındaki fark nedir?
Fark, eşit değerlerin ele alınışındadır. ROW_NUMBER eşit olsalar bile her satıra benzersiz numara verir. RANK ve DENSE_RANK eşitlere aynı sırayı verir; ayrım bir sonraki satırda görülür: RANK numarayı atlar, DENSE_RANK atlamaz.
Pencere fonksiyonunu WHERE içinde neden kullanamıyorum?
Sorgunun işleme sırasında WHERE, pencere fonksiyonları hesaplanmadan önce çalışır; bu yüzden fonksiyon sonucu henüz yoktur. PostgreSQL belgesine göre pencere fonksiyonları yalnızca SELECT listesinde ve ORDER BY'da geçerlidir. Çözüm, sorguyu CTE ya da alt sorguya sarıp filtreyi dışarıda yapmaktır.
Her grupta en yüksek kaydı nasıl getiririm?
PARTITION BY ile grubu, ORDER BY ile sıralamayı verip ROW_NUMBER çalıştırın, sonra sonucu bir CTE içine alın ve dışarıda sıra değerini ilk sıraya eşitleyin. Eşit değerlerde hangi satırın seçileceği önemliyse ORDER BY'a ikinci bir sütun ekleyin.
Maaşı en yüksek ikinci kişi nasıl bulunur?
DENSE_RANK ile maaşa göre azalan sıralayıp bir CTE'ye alın ve dışarıda sıra değerini ikinci sıraya eşitleyin. DENSE_RANK eşit maaşları aynı sıraya koyar ve boşluk bırakmaz, böylece ikinci farklı maaşı alan herkes gelir. ROW_NUMBER bu soruda yanlış sonuç verebilir.
Büyük tablolarda ROW_NUMBER yavaşlar mı?
Evet, sıralama maliyetlidir. Tüm tabloyu numaralayıp sonra filtrelemek, sıralama sütununda uygun indeks yoksa yavaşlayabilir. PARTITION BY ve ORDER BY sütunlarına uygun indeks kurmak ve yürütme planını incelemek ilk adımdır. Büyük sayfalamada anahtar tabanlı yöntemler de değerlendirilir.
İlgili rehberler
İlgili eğitimler
Kategori: Veri Analitiği ve İş Zekası