“Her departmanın en yüksek maaşlı üç çalışanı”, “her müşterinin son beş siparişi”, “her ürün grubunun en çok satan on ürünü”… Bunlar aynı sorunun farklı biçimleridir: grup başına ilk N kayıt. T-SQL’de bunu çözmenin birden fazla yolu vardır ve hepsi aynı sonucu vermez. Eşitlikler (aynı maaşa sahip iki çalışan) nasıl ele alınıyor, sorgu hangi index’i kullanıyor ve büyük tablolarda ne kadar okuma yapıyor, seçimi belirleyen sorulardır.
Bu yazıda bir örnek üzerinden üç yöntemi, eşitlik davranışlarını ve performans farklarını karşılaştırıyoruz.
Raporlama sorgularınız yavaş mı?
Aryasoft, raporlama sorgularınızı yeniden yazar ve doğru index'lerle hem doğru hem hızlı sonuç üretir hale getirir.
Örnek Veri
CREATE TABLE dbo.Departman (
DepartmanId int PRIMARY KEY,
DepartmanAdi nvarchar(50) NOT NULL
);
CREATE TABLE dbo.Calisan (
CalisanId int PRIMARY KEY,
AdSoyad nvarchar(100) NOT NULL,
DepartmanId int NOT NULL REFERENCES dbo.Departman (DepartmanId),
Maas decimal(19, 2) NOT NULL
);
Hedef: her departmanın en yüksek maaşlı üç çalışanını, departman adıyla birlikte listelemek.
Yöntem 1: ROW_NUMBER
WITH sirali AS (
SELECT c.CalisanId, c.AdSoyad, c.DepartmanId, c.Maas,
ROW_NUMBER() OVER (PARTITION BY c.DepartmanId
ORDER BY c.Maas DESC, c.CalisanId) AS sira
FROM dbo.Calisan AS c
)
SELECT d.DepartmanAdi, s.AdSoyad, s.Maas, s.sira
FROM sirali AS s
JOIN dbo.Departman AS d ON d.DepartmanId = s.DepartmanId
WHERE s.sira <= 3
ORDER BY d.DepartmanAdi, s.sira;
Her departmandan tam olarak üç satır döner. Aynı maaşa sahip çalışanlar varsa, ORDER BY içindeki ikinci kolon (CalisanId) hangisinin listeye gireceğini belirler. Bu ikinci kolon olmadan sonuç çalıştırmadan çalıştırmaya değişebilir.
Yöntem 2: DENSE_RANK ile Eşitlikleri Dahil Etmek
“En yüksek üç maaş seviyesindeki herkes” isteniyorsa, eşitlikleri dahil eden bir sıralama fonksiyonu kullanın:
WITH sirali AS (
SELECT c.AdSoyad, c.DepartmanId, c.Maas,
DENSE_RANK() OVER (PARTITION BY c.DepartmanId ORDER BY c.Maas DESC) AS sira
FROM dbo.Calisan AS c
)
SELECT d.DepartmanAdi, s.AdSoyad, s.Maas, s.sira
FROM sirali AS s
JOIN dbo.Departman AS d ON d.DepartmanId = s.DepartmanId
WHERE s.sira <= 3
ORDER BY d.DepartmanAdi, s.sira;
| Fonksiyon | Eşit maaşlar | Sonuç sayısı |
|---|---|---|
| ROW_NUMBER | Farklı numaralar alır; biri dışarıda kalabilir | Grup başına tam N |
| RANK | Aynı numarayı alır, sonraki numara atlanır (1, 1, 3) | N’den fazla olabilir |
| DENSE_RANK | Aynı numarayı alır, atlama olmaz (1, 1, 2) | İlk N seviyedeki herkes |
Not
Hangi fonksiyonun doğru olduğu teknik değil, iş kuralı sorusudur. “En yüksek üç maaş” ile “en yüksek maaşlı üç kişi” farklı sonuçlar üretir. Raporu isteyen kişiyle eşitliklerin nasıl ele alınacağını netleştirin.
Yöntem 3: CROSS APPLY ve TOP
Departman sayısı az, departman başına çalışan sayısı çoksa, her departman için ayrı bir TOP sorgusu çalıştırmak çok daha az okumayla sonuç verebilir:
SELECT d.DepartmanAdi, t.AdSoyad, t.Maas
FROM dbo.Departman AS d
CROSS APPLY (
SELECT TOP (3) c.AdSoyad, c.Maas
FROM dbo.Calisan AS c
WHERE c.DepartmanId = d.DepartmanId
ORDER BY c.Maas DESC, c.CalisanId
) AS t
ORDER BY d.DepartmanAdi, t.Maas DESC;
Eşitlikleri dahil etmek için TOP (3) WITH TIES kullanılabilir; bu durumda ORDER BY yalnızca Maas DESC olmalıdır.
Performansın Anahtarı: Doğru Index
Üç yöntem de aynı index’ten yararlanır: gruplama kolonu önde, sıralama kolonu ikinci sırada ve döndürülen kolonlar INCLUDE ile.
CREATE NONCLUSTERED INDEX IX_Calisan_Departman_Maas
ON dbo.Calisan (DepartmanId, Maas DESC)
INCLUDE (AdSoyad);
- ROW_NUMBER ve DENSE_RANK: Index sayesinde ayrı bir sıralama işlemine gerek kalmaz; ama yine de tablonun tüm satırları okunur ve numaralandırılır.
- CROSS APPLY + TOP: Her departman için index’te doğrudan ilgili yere gidip yalnızca üç satır okur. Departman sayısı azken çok daha az okuma yapar; departman sayısı çok fazlaysa bu avantaj azalır.
İpucu
İki yöntemi kendi verinizde SET STATISTICS IO ON ile karşılaştırın; logical reads farkı hangi yöntemin daha uygun olduğunu açıkça gösterir. Ayrıntılar okumalar yazımızda.
Özet
- Grup başına tam N satır: ROW_NUMBER, ORDER BY benzersiz bir kolonla bitmeli.
- Eşitlikler dahil: DENSE_RANK ya da TOP … WITH TIES.
- Az sayıda grup, grup başına çok satır: CROSS APPLY + TOP genellikle en az okumayı yapar.
- Her durumda: gruplama ve sıralama kolonlarına uygun bir index.
İlgili Yazılar
SQL Server Performans Danışmanlığı
Raporlarınız hem doğru hem hızlı olsun
Aryasoft’un kıdemli DBA ekibi raporlama sorgularınızı iş kurallarına uygun şekilde yeniden yazar, doğru index’lerle okumaları azaltır ve sonuçları ölçerek doğrular.