---
title: 'Sıralama İşlevleri'
source: 'https://academia.sh/tr/kurslar/ileri-sql/siralama-islevleri'
course: 'İleri SQL'
language: tr
updated: '2026-08-17T18:08:54+00:00'
license: 'CC BY-SA 4.0'
---

# Sıralama İşlevleri

Satır numarası, sıra ve yoğun sıra işlevlerinin beraberlik davranışları, dilim ve yüzdelik dağılım işlevleri, bölüm içinde ilk N seçimi ve satır numarasıyla tekilleştirme.

Önceki ders pencerenin üzerinde hesap yürüttü ama satırlara sıra vermedi. "En çok ödünç
alan üç üye" gibi sorular ise önce bir sıra numarası ister; hemen ardından da bir karar
gerektirir: eşit sayıya sahip iki üye aynı sırayı mı almalıdır, farklı mı? Bu soruya üç
ayrı yanıt verilebilir ve SQL üçünü de ayrı işlev olarak tanımlar.

Bu ders, sıralama işlevlerini aynı veri üzerinde yan yana koyup farklarını gösterir ve
sıralamanın belirlenimci olması için gereken koşulu kurar.

## Üç İşlev, Üç Beraberlik Kararı

Üç işlev de pencereyi `ORDER BY` ile sıralar ve her satıra bir tam sayı verir. Ayrıldıkları
nokta, sıralama anahtarında **eşit değerli** satırlarla ne yapıldığıdır:

- `ROW_NUMBER`: eşlik tanımaz. Her satıra farklı bir numara verir; 1, 2, 3 diye sürer.
- `RANK`: eşlere aynı sırayı verir, sonra **atlar**. İki satır ikinci olursa sonraki
  dördüncüdür.
- `DENSE_RANK`: eşlere aynı sırayı verir, **atlamaz**. İki satır ikinci olursa sonraki
  üçüncüdür.

Üçü de aynı sorguda yazılabilir; ortak pencere `WINDOW` yan tümcesinde bir kez tanımlanır:

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE uye(id INTEGER PRIMARY KEY, ad TEXT, sube TEXT);
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
INSERT INTO uye VALUES (1,'Ayse','Kadikoy'),(2,'Burak','Kadikoy'),(3,'Ceren','Uskudar'),
  (4,'Deniz','Uskudar'),(5,'Emre','Besiktas'),(6,'Fatma','Besiktas'),
  (7,'Gokhan','Kadikoy'),(8,'Hale','Uskudar');
INSERT INTO odunc VALUES
  (1,1,1,'2024-03-01','2024-03-15'),(2,3,1,'2024-03-01','2024-03-20'),
  (3,5,1,'2024-03-04','2024-03-18'),(4,7,1,'2024-03-11',NULL),
  (5,9,1,'2024-03-18','2024-03-29'),(6,2,2,'2024-03-01','2024-03-12'),
  (7,4,2,'2024-03-06','2024-03-25'),(8,6,2,'2024-03-11','2024-03-19'),
  (9,8,2,'2024-03-21',NULL),(10,1,3,'2024-03-04','2024-03-10'),
  (11,3,3,'2024-03-06','2024-03-27'),(12,10,3,'2024-03-13','2024-03-22'),
  (13,2,3,'2024-03-25',NULL),(14,5,4,'2024-03-04','2024-03-09'),
  (15,7,4,'2024-03-13','2024-03-26'),(16,4,4,'2024-03-20',NULL),
  (17,6,5,'2024-03-06','2024-03-14'),(18,9,5,'2024-03-11','2024-03-23'),
  (19,1,5,'2024-03-25','2024-03-28'),(20,8,6,'2024-03-04','2024-03-17'),
  (21,10,6,'2024-03-13','2024-03-21'),(22,3,6,'2024-03-20',NULL),
  (23,2,7,'2024-03-11','2024-03-16'),(24,5,7,'2024-03-18','2024-03-24'),
  (25,4,8,'2024-03-13','2024-03-24');

WITH sayim AS (
  SELECT u.ad, u.sube, COUNT(o.id) AS adet
  FROM uye u LEFT JOIN odunc o ON o.uye_id = u.id
  GROUP BY u.id, u.ad, u.sube
)
SELECT ad, adet,
       ROW_NUMBER() OVER s AS satir_no,
       RANK()       OVER s AS sira,
       DENSE_RANK() OVER s AS yogun_sira
FROM sayim
WINDOW s AS (ORDER BY adet DESC)
ORDER BY adet DESC, ad;
SQL
```

```text
┌────────┬──────┬──────────┬──────┬────────────┐
│   ad   │ adet │ satir_no │ sira │ yogun_sira │
├────────┼──────┼──────────┼──────┼────────────┤
│ Ayse   │ 5    │ 1        │ 1    │ 1          │
│ Burak  │ 4    │ 2        │ 2    │ 2          │
│ Ceren  │ 4    │ 3        │ 2    │ 2          │
│ Deniz  │ 3    │ 4        │ 4    │ 3          │
│ Emre   │ 3    │ 5        │ 4    │ 3          │
│ Fatma  │ 3    │ 6        │ 4    │ 3          │
│ Gokhan │ 2    │ 7        │ 7    │ 4          │
│ Hale   │ 1    │ 8        │ 8    │ 5          │
└────────┴──────┴──────────┴──────┴────────────┘
```

Dört ödünç alan iki üye var. `RANK` ikisine de 2 verdi ve sonraki üyeye 3 değil 4 verdi:
üçüncülük tüketilmiş sayıldı. `DENSE_RANK` ise ikisine 2 verip sonrakine 3 verdi; sıra
numaraları boşluksuz ilerledi. `ROW_NUMBER` beraberliği hiç görmedi ve 2 ile 3'ü keyfî
biçimde dağıttı.

Bu üç sütun üç ayrı soruya karşılık gelir. `RANK`, "kaç üye benden fazla aldı" sorusunun
yanıtına bir eklenmiş hâlidir — yarışma sıralamalarının alışkanlığı budur. `DENSE_RANK`,
"kaç farklı değer benden büyüktür" sorusunu yanıtlar ve seviye saymak için uygundur.
`ROW_NUMBER` sıra değil, **kimlik** üretir: satırları birbirinden ayırmak için kullanılır.

Son sütunda `Hale` için `RANK` 8, `DENSE_RANK` 5 veriyor. İki sayı arasındaki fark
beraberliklerin toplam etkisidir: sekiz üye ama beş farklı değer var.

## Beraberliğin Belirsizliği

`ROW_NUMBER` sütununda `Burak` 2, `Ceren` 3 aldı. Bu dağılım sorgunun yazımından çıkmaz:
ikisinin de `adet` değeri 4 olduğuna göre `ORDER BY adet DESC` ikisini ayırmaz. Numaraların
hangisine gideceğine motor karar verdi.

Bu, kaçınılması gereken bir durumdur. Aynı sorgu farklı bir plan altında, farklı bir sürümde
ya da tablo farklı sırada okunduğunda numaraları takas edebilir. `ROW_NUMBER` bir sonuç
kümesini sayfalamak ya da tekilleştirmek için kullanılıyorsa, bu takas sessiz bir hataya
dönüşür: sayfalar arasında bir kayıt iki kez görünür, bir başkası hiç görünmez.

Kural açıktır: `ROW_NUMBER` kullanılan pencerede `ORDER BY` anahtarı satırları **tek
biçimde** belirlemelidir. Anahtar bunu sağlamıyorsa sonuna birincil anahtar gibi tekil bir
sütun eklenir — `ORDER BY adet DESC, ad` yazımı yukarıdaki belirsizliği ortadan kaldırır.
`RANK` ve `DENSE_RANK` bu sorundan etkilenmez, çünkü eş satırlara zaten aynı değeri
verirler.

## Dilim ve Yüzdelik Dağılım

Sıralama ailesinin diğer üyeleri, satırın sıralamadaki **konumunu** oransal olarak verir.
`NTILE(n)` bölümü olabildiğince eşit `n` parçaya böler; `PERCENT_RANK` sıranın sıfır ile
bir arasındaki karşılığını, `CUME_DIST` ise "bu satır dâhil, buraya kadar olan satırların
oranını" verir:

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE uye(id INTEGER PRIMARY KEY, ad TEXT, sube TEXT);
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
INSERT INTO uye VALUES (1,'Ayse','Kadikoy'),(2,'Burak','Kadikoy'),(3,'Ceren','Uskudar'),
  (4,'Deniz','Uskudar'),(5,'Emre','Besiktas'),(6,'Fatma','Besiktas'),
  (7,'Gokhan','Kadikoy'),(8,'Hale','Uskudar');
INSERT INTO odunc VALUES
  (1,1,1,'2024-03-01','2024-03-15'),(2,3,1,'2024-03-01','2024-03-20'),
  (3,5,1,'2024-03-04','2024-03-18'),(4,7,1,'2024-03-11',NULL),
  (5,9,1,'2024-03-18','2024-03-29'),(6,2,2,'2024-03-01','2024-03-12'),
  (7,4,2,'2024-03-06','2024-03-25'),(8,6,2,'2024-03-11','2024-03-19'),
  (9,8,2,'2024-03-21',NULL),(10,1,3,'2024-03-04','2024-03-10'),
  (11,3,3,'2024-03-06','2024-03-27'),(12,10,3,'2024-03-13','2024-03-22'),
  (13,2,3,'2024-03-25',NULL),(14,5,4,'2024-03-04','2024-03-09'),
  (15,7,4,'2024-03-13','2024-03-26'),(16,4,4,'2024-03-20',NULL),
  (17,6,5,'2024-03-06','2024-03-14'),(18,9,5,'2024-03-11','2024-03-23'),
  (19,1,5,'2024-03-25','2024-03-28'),(20,8,6,'2024-03-04','2024-03-17'),
  (21,10,6,'2024-03-13','2024-03-21'),(22,3,6,'2024-03-20',NULL),
  (23,2,7,'2024-03-11','2024-03-16'),(24,5,7,'2024-03-18','2024-03-24'),
  (25,4,8,'2024-03-13','2024-03-24');

WITH sayim AS (
  SELECT u.ad, COUNT(o.id) AS adet
  FROM uye u LEFT JOIN odunc o ON o.uye_id = u.id
  GROUP BY u.id, u.ad
)
SELECT ad, adet,
       NTILE(4) OVER s AS dilim,
       ROUND(PERCENT_RANK() OVER s, 2) AS yuzdelik_sira,
       ROUND(CUME_DIST()    OVER s, 2) AS birikimli_oran
FROM sayim
WINDOW s AS (ORDER BY adet DESC)
ORDER BY adet DESC, ad;
SQL
```

```text
┌────────┬──────┬───────┬───────────────┬────────────────┐
│   ad   │ adet │ dilim │ yuzdelik_sira │ birikimli_oran │
├────────┼──────┼───────┼───────────────┼────────────────┤
│ Ayse   │ 5    │ 1     │ 0.0           │ 0.13           │
│ Burak  │ 4    │ 1     │ 0.14          │ 0.38           │
│ Ceren  │ 4    │ 2     │ 0.14          │ 0.38           │
│ Deniz  │ 3    │ 2     │ 0.43          │ 0.75           │
│ Emre   │ 3    │ 3     │ 0.43          │ 0.75           │
│ Fatma  │ 3    │ 3     │ 0.43          │ 0.75           │
│ Gokhan │ 2    │ 4     │ 0.86          │ 0.88           │
│ Hale   │ 1    │ 4     │ 1.0           │ 1.0            │
└────────┴──────┴───────┴───────────────┴────────────────┘
```

Dikkat çeken satır `Burak` ile `Ceren`: aynı `adet` değerine sahipler, yüzdelik sıraları da
birikimli oranları da aynı, ama farklı dilimlere düştüler. `NTILE` eşleri gözetmez;
satırları sayıya göre böler ve bölüm sınırı bir eş kümesinin ortasına düşerse onları
ayırır. Bu yüzden dilim numarası, eşit değerli satırlar için `ROW_NUMBER` kadar
belirsizdir.

`PERCENT_RANK` ilk satıra her zaman 0, `CUME_DIST` son satıra her zaman 1 verir. İkisi
arasındaki fark tanımlarındadır: yüzdelik sıra "önümde kaç satır var" oranını, birikimli
oran "benim ve benden büyük olmayanların" oranını ölçer.

## Bölüm İçinde İlk N

Sıralama işlevlerinin en yaygın kullanımı, her bölümün en iyilerini seçmektir. Önceki
derste belirtildiği gibi pencere işlevinin sonucu aynı sorgunun `WHERE` yan tümcesinde
kullanılamaz; sıralama bir ortak tablo ifadesinde yapılır, süzme dışarıda:

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE uye(id INTEGER PRIMARY KEY, ad TEXT, sube TEXT);
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
INSERT INTO uye VALUES (1,'Ayse','Kadikoy'),(2,'Burak','Kadikoy'),(3,'Ceren','Uskudar'),
  (4,'Deniz','Uskudar'),(5,'Emre','Besiktas'),(6,'Fatma','Besiktas'),
  (7,'Gokhan','Kadikoy'),(8,'Hale','Uskudar');
INSERT INTO odunc VALUES
  (1,1,1,'2024-03-01','2024-03-15'),(2,3,1,'2024-03-01','2024-03-20'),
  (3,5,1,'2024-03-04','2024-03-18'),(4,7,1,'2024-03-11',NULL),
  (5,9,1,'2024-03-18','2024-03-29'),(6,2,2,'2024-03-01','2024-03-12'),
  (7,4,2,'2024-03-06','2024-03-25'),(8,6,2,'2024-03-11','2024-03-19'),
  (9,8,2,'2024-03-21',NULL),(10,1,3,'2024-03-04','2024-03-10'),
  (11,3,3,'2024-03-06','2024-03-27'),(12,10,3,'2024-03-13','2024-03-22'),
  (13,2,3,'2024-03-25',NULL),(14,5,4,'2024-03-04','2024-03-09'),
  (15,7,4,'2024-03-13','2024-03-26'),(16,4,4,'2024-03-20',NULL),
  (17,6,5,'2024-03-06','2024-03-14'),(18,9,5,'2024-03-11','2024-03-23'),
  (19,1,5,'2024-03-25','2024-03-28'),(20,8,6,'2024-03-04','2024-03-17'),
  (21,10,6,'2024-03-13','2024-03-21'),(22,3,6,'2024-03-20',NULL),
  (23,2,7,'2024-03-11','2024-03-16'),(24,5,7,'2024-03-18','2024-03-24'),
  (25,4,8,'2024-03-13','2024-03-24');

WITH sayim AS (
  SELECT u.ad, u.sube, COUNT(o.id) AS adet
  FROM uye u LEFT JOIN odunc o ON o.uye_id = u.id
  GROUP BY u.id, u.ad, u.sube
),
siralanmis AS (
  SELECT sube, ad, adet,
         RANK()       OVER b AS sira,
         ROW_NUMBER() OVER b AS satir_no
  FROM sayim
  WINDOW b AS (PARTITION BY sube ORDER BY adet DESC)
)
SELECT sube, ad, adet, sira, satir_no FROM siralanmis
WHERE sira <= 1 ORDER BY sube, ad;
SQL
```

```text
┌──────────┬───────┬──────┬──────┬──────────┐
│   sube   │  ad   │ adet │ sira │ satir_no │
├──────────┼───────┼──────┼──────┼──────────┤
│ Besiktas │ Emre  │ 3    │ 1    │ 1        │
│ Besiktas │ Fatma │ 3    │ 1    │ 2        │
│ Kadikoy  │ Ayse  │ 5    │ 1    │ 1        │
│ Uskudar  │ Ceren │ 4    │ 1    │ 1        │
└──────────┴───────┴──────┴──────┴──────────┘
```

Beşiktaş'tan iki satır geldi, diğerlerinden birer satır. Bunun nedeni işlev seçimidir:
`RANK` ile süzülünce beraberlikteki tüm satırlar gelir; `WHERE satir_no <= 1` yazılsaydı
`Fatma` düşerdi ve hangisinin düştüğü belirsiz olurdu.

Seçim, sorunun ne istediğine bağlıdır. "Her şubenin en çok ödünç alan üyesi" sorusu
beraberlikte iki ad istiyorsa `RANK`, tam olarak bir satır istiyorsa `ROW_NUMBER` uygundur —
ikinci durumda beraberliği bozan ek bir anahtar da belirtilmelidir. `WHERE sira <= 3`
biçimindeki eşiği değiştirmek, aynı sorguyu ilk üçe genişletir.

## Satır Numarasıyla Tekilleştirme

`ROW_NUMBER` bir sıralama değil ayıklama aracı olarak da kullanılır. Bölüm içinde
numaralanan satırlardan yalnız birincisini almak, "her grubun bir temsilcisi" desenidir:

```bash
sqlite3 -box -header <<'SQL'
CREATE TABLE uye(id INTEGER PRIMARY KEY, ad TEXT, sube TEXT);
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT, iade TEXT);
INSERT INTO uye VALUES (1,'Ayse','Kadikoy'),(2,'Burak','Kadikoy'),(3,'Ceren','Uskudar'),
  (4,'Deniz','Uskudar'),(5,'Emre','Besiktas'),(6,'Fatma','Besiktas'),
  (7,'Gokhan','Kadikoy'),(8,'Hale','Uskudar');
INSERT INTO odunc VALUES
  (1,1,1,'2024-03-01','2024-03-15'),(2,3,1,'2024-03-01','2024-03-20'),
  (3,5,1,'2024-03-04','2024-03-18'),(4,7,1,'2024-03-11',NULL),
  (5,9,1,'2024-03-18','2024-03-29'),(6,2,2,'2024-03-01','2024-03-12'),
  (7,4,2,'2024-03-06','2024-03-25'),(8,6,2,'2024-03-11','2024-03-19'),
  (9,8,2,'2024-03-21',NULL),(10,1,3,'2024-03-04','2024-03-10'),
  (11,3,3,'2024-03-06','2024-03-27'),(12,10,3,'2024-03-13','2024-03-22'),
  (13,2,3,'2024-03-25',NULL),(14,5,4,'2024-03-04','2024-03-09'),
  (15,7,4,'2024-03-13','2024-03-26'),(16,4,4,'2024-03-20',NULL),
  (17,6,5,'2024-03-06','2024-03-14'),(18,9,5,'2024-03-11','2024-03-23'),
  (19,1,5,'2024-03-25','2024-03-28'),(20,8,6,'2024-03-04','2024-03-17'),
  (21,10,6,'2024-03-13','2024-03-21'),(22,3,6,'2024-03-20',NULL),
  (23,2,7,'2024-03-11','2024-03-16'),(24,5,7,'2024-03-18','2024-03-24'),
  (25,4,8,'2024-03-13','2024-03-24');

WITH numarali AS (
  SELECT id, uye_id, alis,
         ROW_NUMBER() OVER (PARTITION BY uye_id ORDER BY alis DESC, id DESC) AS no
  FROM odunc
)
SELECT n.uye_id, u.ad, n.id AS odunc_id, n.alis
FROM numarali n JOIN uye u ON u.id = n.uye_id
WHERE n.no = 1 ORDER BY n.uye_id;
SQL
```

```text
┌────────┬────────┬──────────┬────────────┐
│ uye_id │   ad   │ odunc_id │    alis    │
├────────┼────────┼──────────┼────────────┤
│ 1      │ Ayse   │ 5        │ 2024-03-18 │
│ 2      │ Burak  │ 9        │ 2024-03-21 │
│ 3      │ Ceren  │ 13       │ 2024-03-25 │
│ 4      │ Deniz  │ 16       │ 2024-03-20 │
│ 5      │ Emre   │ 19       │ 2024-03-25 │
│ 6      │ Fatma  │ 22       │ 2024-03-20 │
│ 7      │ Gokhan │ 24       │ 2024-03-18 │
│ 8      │ Hale   │ 25       │ 2024-03-13 │
└────────┴────────┴──────────┴────────────┘
```

Sıralama anahtarı `alis DESC, id DESC` biçiminde iki sütunludur. `alis` tek başına aynı
üye içinde yinelenebilir; `id` birincil anahtar olduğu için eklenmesi sonucu belirlenimci
kılar. Bu, önceki bölümdeki kuralın uygulanmış hâlidir.

Aynı sonuç, İlişkili Alt Sorgular dersindeki `MAX` alt sorgusuyla da üretilebilirdi. İki
yazımın farkı, beraberlik durumunda ortaya çıkar: alt sorgulu yazım aynı tarihe sahip iki
kaydın ikisini de döndürür, satır numarasıyla yazım tam olarak bir tanesini seçer.

## Özet

- `ROW_NUMBER` eşlik tanımaz, `RANK` eşlere aynı sırayı verip atlar, `DENSE_RANK` eşlere
  aynı sırayı verip atlamaz.
- `RANK` "kaç satır benden önde", `DENSE_RANK` "kaç farklı değer benden önde" sorusunu
  yanıtlar; `ROW_NUMBER` sıra değil kimlik üretir.
- `ROW_NUMBER` ve `NTILE` sonuçları, sıralama anahtarı satırları tek biçimde belirlemiyorsa
  belirlenimci değildir; anahtara tekil bir sütun eklenmelidir.
- Pencere sonucuna göre süzmek için sorgu bir ortak tablo ifadesine sarılır; bölüm içinde
  ilk N seçiminde `RANK` beraberlikteki tüm satırları, `ROW_NUMBER` tam olarak N satırı
  getirir.
- `PERCENT_RANK` ilk satıra 0, `CUME_DIST` son satıra 1 verir; `NTILE` eş satırları farklı
  dilimlere ayırabilir.

## Sonraki Adım

Buraya kadarki her sorgunun sonucu, sütunları sorgu yazılırken belli olan bir tabloydu.
Raporlarda ise sık sık tersi istenir: satırlardaki değerler **sütun başlığına** çıksın,
her tür ya da her ay ayrı bir sütun olsun. Bu dönüşüm, satır sayısını azaltıp sütun sayısını
artırır ve SQL'in sabit sütun listesi kuralıyla doğrudan çelişir. Sonraki ders, döndürme
işlemlerini koşullu toplamayla nasıl yazıldığını ve ters yönde satıra dönüştürmenin nasıl
yapıldığını gösterecek.
