SQL ROW_NUMBER, RANK ve DENSE_RANK Farkı


ROW_NUMBER, RANK ve DENSE_RANK satırlara sıralama numarası veren SQL window function’larıdır. Aralarındaki temel fark, aynı değere sahip satırlara nasıl sıra verdikleridir.

Kısa fark

FonksiyonEşit değerlere aynı sıraSonraki sırada boşluk
ROW_NUMBERHayırHayır
RANKEvetEvet
DENSE_RANKEvetHayır

Satış tutarları 1000, 900, 900, 700 ise:

TutarROW_NUMBERRANKDENSE_RANK
1000111
900222
900322
700443

ROW_NUMBER kullanımı

SELECT
    ProductName,
    SalesAmount,
    ROW_NUMBER() OVER (
        ORDER BY SalesAmount DESC
    ) AS RowNo
FROM ProductSales;

Her satır benzersiz sıra numarası alır. Eşit tutarlar olsa bile numaralar tekrarlanmaz.

Sonucun her çalışmada tutarlı olması için ORDER BY bölümüne benzersiz bir ikinci alan eklemek iyi olur:

ROW_NUMBER() OVER (
    ORDER BY SalesAmount DESC, ProductID
)

RANK kullanımı

SELECT
    ProductName,
    SalesAmount,
    RANK() OVER (
        ORDER BY SalesAmount DESC
    ) AS SalesRank
FROM ProductSales;

Eşit tutarlar aynı sırayı alır. Sonraki sıra atlanır. Yarışma sıralaması mantığına uygundur.

DENSE_RANK kullanımı

SELECT
    ProductName,
    SalesAmount,
    DENSE_RANK() OVER (
        ORDER BY SalesAmount DESC
    ) AS SalesRank
FROM ProductSales;

Eşit tutarlar aynı sırayı alır ancak sonraki sıra atlanmaz.

PARTITION BY ne işe yarar?

PARTITION BY, sıralamayı gruplar içinde yeniden başlatır:

SELECT
    Category,
    ProductName,
    SalesAmount,
    ROW_NUMBER() OVER (
        PARTITION BY Category
        ORDER BY SalesAmount DESC
    ) AS CategoryRowNo
FROM ProductSales;

Her kategori kendi içinde 1’den başlayarak sıralanır.

Her gruptan ilk 3 kayıt

Window function sonucu aynı SELECT’in WHERE bölümünde doğrudan filtrelenemeyeceği için CTE kullanılabilir:

WITH RankedProducts AS (
    SELECT
        Category,
        ProductName,
        SalesAmount,
        ROW_NUMBER() OVER (
            PARTITION BY Category
            ORDER BY SalesAmount DESC
        ) AS RowNo
    FROM ProductSales
)
SELECT *
FROM RankedProducts
WHERE RowNo <= 3;

Bu desen “her müşterinin son siparişi”, “her kategorinin en çok satan üç ürünü” ve “her çalışanın son kaydı” gibi sorularda sık kullanılır.

CTE yapısını ayrıntılı öğrenmek için SQL CTE ve WITH rehberine bakın.

Tekrarlı kayıtlardan birini seçmek

WITH DuplicateRows AS (
    SELECT
        *,
        ROW_NUMBER() OVER (
            PARTITION BY Email
            ORDER BY UpdatedAt DESC
        ) AS RowNo
    FROM Customers
)
SELECT *
FROM DuplicateRows
WHERE RowNo = 1;

Her e-posta için en güncel kayıt seçilir. Silme işleminden önce tekillik kuralı ve veri kalitesi mutlaka doğrulanmalıdır.

Hangi fonksiyon seçilmeli?

  • Her satıra benzersiz sıra gerekiyorsa: ROW_NUMBER
  • Eşit sonuçlar aynı dereceyi alsın ve sıra atlansın isteniyorsa: RANK
  • Eşit sonuçlar aynı dereceyi alsın ancak sıra atlanmasın isteniyorsa: DENSE_RANK

Sık yapılan hatalar

ORDER BY kullanmamak

Bu fonksiyonlar OVER içindeki sıralama tanımına ihtiyaç duyar.

Eşit değerlerde sonucu deterministik sanmak

ROW_NUMBER eşit değerlerde benzersiz ikinci sıralama alanı yoksa satırlara farklı çalışmalarda farklı numara verebilir.

GROUP BY ile karıştırmak

GROUP BY satırları tek sonuçta toplar. Window function satırları koruyarak hesaplama yapar.

Büyük partition’ları dikkate almamak

Geniş sıralamalar bellek ve işlem maliyeti oluşturabilir. Filtre, indeks ve yürütme planı kontrol edilmelidir.

Uygulama

Temel SELECT, filtreleme, JOIN ve toplulaştırma alıştırmalarını ücretsiz SQL eğitiminde çalıştırabilirsiniz.

Sık sorulan sorular

ROW_NUMBER aynı numarayı iki kez verir mi?

Aynı sonuç kümesi içinde hayır; her satır farklı numara alır.

RANK neden 2’den sonra 4 verir?

İkinci sırayı iki kayıt paylaştığı için sonraki derece dördüncü sıradan devam eder.

PARTITION BY zorunlu mu?

Hayır. Kullanılmazsa tüm sonuç tek grup olarak sıralanır.

Ali Rıza Ekici

İş Zekâsı, Qlik Sense, ETL, SAP, SQL ve veri analitiği üzerine uygulamalı içerikler hazırlıyor.

Yazar ve deneyim hakkında →