SQL veritabanı yönetim sistemlerinde veri analizi yaparken karşılaşılan birçok karmaşık senaryo, geleneksel GROUP BY yapısıyla çözülememektedir. İşte tam bu noktada SQL Pencere Fonksiyonları (Window Functions) devreye girer. Bu güçlü araçlar, veri setinin tamamını veya belirli bölümlerini analiz ederken her bir satırın kendi bağlamında değerlendirilmesine olanak tanır. Özellikle detaylı raporlama ve ileri düzey analizler için vazgeçilmez bir yardımcıdır.
SQL Pencere Fonksiyonları Neden Gereklidir?
Geleneksel SQL sorgularında kullanılan GROUP BY ifadesi, satırları belirli kriterlere göre gruplar ve her grup için tek bir özet değer (örneğin toplam, ortalama) döndürür. Ancak bu işlem, gruplanan satırların detaylarını kaybetmemize neden olur. Örneğin, bir departmandaki her çalışanın maaşını gösterirken aynı zamanda departmanın ortalama maaşını da görmek istediğimizde GROUP BY yetersiz kalır. Çünkü GROUP BY kullandığımızda ya tüm çalışanların listesini ya da sadece departman ortalamasını görebiliriz, ikisini bir arada aynı satırda görüntüleyemeyiz. Pencere Fonksiyonları, bu kısıtlamayı ortadan kaldırarak her bir orijinal satırı korurken, aynı zamanda belirli bir “pencere” (window) içindeki diğer satırlara göre hesaplamalar yapmamızı sağlar.
Pencere Fonksiyonları Nedir ve Nasıl Çalışır?
Pencere fonksiyonları, belirli bir satır kümesi üzerinde işlem yapan ve her bir orijinal satır için bir değer döndüren fonksiyonlardır. Bu satır kümesi, ilgili satırla ilişkili olan ve OVER() yan tümcesi ile tanımlanan bir “pencere”dir. Geleneksel aggregate fonksiyonlarından (SUM, AVG, COUNT vb.) temel farkı, satırları gruplayıp tek bir sonuç satırı döndürmek yerine, her orijinal satır için bir sonuç döndürmesidir.
OVER() Yan Tümcesi
Pencere fonksiyonlarının kalbi OVER() yan tümcesidir. Bu yan tümce, fonksiyonun hangi satırlar üzerinde çalışacağını, yani “pencereyi” nasıl tanımlayacağını belirtir. OVER() kendi başına kullanıldığında, tüm sonuç kümesini tek bir pencere olarak kabul eder. Ancak genellikle içine aşağıdaki gibi ek parametreler alır:
PARTITION BY
PARTITION BY ifadesi, sonuç kümesini mantıksal bölümlere ayırır. Her bölüm, pencere fonksiyonunun bağımsız olarak üzerinde çalışacağı bir mini veri kümesi gibidir. Örneğin, çalışanları departmanlarına göre bölümlere ayırırsanız, bir departmanın ortalama maaşı sadece o departman içindeki çalışanlar üzerinden hesaplanır.
SELECT
CalisanAdi,
Departman,
Maas,
AVG(Maas) OVER (PARTITION BY Departman) AS DepartmanOrtalamaMaas
FROM
Calisanlar;
Bu örnekte, her çalışanın kendi maaşı ve ait olduğu departmanın ortalama maaşı aynı sorgu içinde listelenir.
ORDER BY
ORDER BY, PARTITION BY ile oluşturulan her bir bölüm içindeki satırların sıralamasını belirler. Bu sıralama, özellikle sıralama (ranking) fonksiyonları (ROW_NUMBER(), RANK()) ve kümülatif toplamlar veya hareketli ortalamalar gibi sıralamaya bağımlı hesaplamalar için kritiktir.
SELECT
SatisID,
SatisTarihi,
SatisTutari,
SUM(SatisTutari) OVER (ORDER BY SatisTarihi) AS KümülatifToplamSatis
FROM
Satislar;
Bu sorgu, satış tarihine göre sıralanmış kümülatif toplam satışı gösterir.
ROWS / RANGE BETWEEN (Pencere Çerçevesi)
ROWS BETWEEN veya RANGE BETWEEN, PARTITION BY ve ORDER BY ile belirlenen pencere içinde, fonksiyonun tam olarak hangi alt satır kümesi üzerinde işlem yapacağını tanımlar. Bu, “pencere çerçevesi” olarak adlandırılır. Örneğin, “geçerli satır ve önceki 2 satır” gibi özel bir aralık belirtebilirsiniz. En yaygın kullanımlarından bazıları şunlardır:
UNBOUNDED PRECEDING AND CURRENT ROW: Bölümün başından geçerli satıra kadar. (Kümülatif toplamlar için idealdir.)N PRECEDING AND M FOLLOWING: Geçerli satırdan N satır öncesi ile M satır sonrası arası.UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING: Tüm bölümü kapsar. (PARTITION BYile kullanılırsa tüm bölüm için tek bir değer döndürür.)
SELECT
SatisTarihi,
SatisTutari,
AVG(SatisTutari) OVER (ORDER BY SatisTarihi ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS Son3GunlukOrtalama
FROM
GunlukSatislar;
Bu örnek, satış tarihine göre sıralanmış son 3 günlük (mevcut gün dahil) satışların hareketli ortalamasını hesaplar.
Pencere Fonksiyonlarının Türleri
1. Aggregate Pencere Fonksiyonları
Geleneksel aggregate fonksiyonları (SUM(), AVG(), COUNT(), MIN(), MAX()) OVER() yan tümcesi ile kullanıldığında pencere fonksiyonu haline gelir. Bu sayede, aggregate değerini her bir detay satırı için hesaplayabiliriz.
SELECT
UrunAdi,
Kategori,
SatisMiktari,
SUM(SatisMiktari) OVER (PARTITION BY Kategori) AS KategoriToplamSatis,
AVG(SatisMiktari) OVER (PARTITION BY Kategori) AS KategoriOrtalamaSatis
FROM
UrunSatislari;
Her ürün satırında, ürünün kendi satış miktarının yanı sıra ait olduğu kategorinin toplam ve ortalama satış miktarları da gösterilir.
2. Sıralama (Ranking) Pencere Fonksiyonları
Bu fonksiyonlar, belirli bir pencere içindeki satırlara sıralama veya numara atar.
ROW_NUMBER(): Her satıra benzersiz bir sıra numarası atar. Aynı değerlere sahip satırlar farklı sıralar alır.RANK(): Aynı değerlere sahip satırlara aynı sırayı atar ve sonraki sıraları atlar (boşluk bırakır).DENSE_RANK(): Aynı değerlere sahip satırlara aynı sırayı atar, ancak sonraki sıraları atlamaz (boşluk bırakmaz).NTILE(n): Satırları belirtilen ‘n’ sayıda gruba böler ve her gruba bir numara atar.
SELECT
CalisanAdi,
Departman,
Maas,
ROW_NUMBER() OVER (PARTITION BY Departman ORDER BY Maas DESC) AS DepartmanIciMaasSirasi,
RANK() OVER (PARTITION BY Departman ORDER BY Maas DESC) AS DepartmanIciRank,
DENSE_RANK() OVER (PARTITION BY Departman ORDER BY Maas DESC) AS DepartmanIciDenseRank
FROM
Calisanlar;
Bu sorgu, her departman içinde maaşa göre sıralanmış çalışanların farklı sıralama türlerini gösterir.
3. Analitik (Value) Pencere Fonksiyonları
Bu fonksiyonlar, geçerli satırdan önceki, sonraki veya penceredeki ilk/son satırın değerlerini almanızı sağlar.
LAG(expression, offset, default): Geçerli satırdan belirtilen ‘offset’ kadar önceki satırın değerini döndürür.LEAD(expression, offset, default): Geçerli satırdan belirtilen ‘offset’ kadar sonraki satırın değerini döndürür.FIRST_VALUE(expression): Pencere içindeki ilk satırın değerini döndürür.LAST_VALUE(expression): Pencere içindeki son satırın değerini döndürür.
SELECT
SatisTarihi,
SatisTutari,
LAG(SatisTutari, 1, 0) OVER (ORDER BY SatisTarihi) AS OncekiGunSatisTutari,
LEAD(SatisTutari, 1, 0) OVER (ORDER BY SatisTarihi) AS SonrakiGunSatisTutari
FROM
GunlukSatislar;
Bu örnek, her günün satış tutarını, bir önceki günkü ve bir sonraki günkü satış tutarlarıyla karşılaştırmak için LAG ve LEAD fonksiyonlarını kullanır.
Pencere Fonksiyonlarının Pratik Kullanım Alanları ve Faydaları
- Kümülatif Toplamlar ve Hareketli Ortalamalar: Zaman serisi verilerinde belirli bir ana kadar olan toplamları veya belirli bir dönemdeki ortalamaları kolayca hesaplama.
- Gruplar İçinde En İyi/En Kötü N Değerleri Bulma: Her departmanda en yüksek maaş alan ilk 3 çalışanı bulmak gibi.
- Satır Karşılaştırmaları: Bir önceki veya sonraki dönemdeki değerlerle karşılaştırmalar yaparak trend analizi yapma (örneğin aylık satış büyümesi).
- Yüzdelik Dilimler ve Percentil Hesaplamaları:
NTILE()ile veriyi eşit gruplara bölme veyaPERCENT_RANK()gibi fonksiyonlarla değerlerin konumunu belirleme. - Karmaşık İş Mantıklarını Basitleştirme: Birden fazla alt sorgu veya kendi kendine join (self-join) gerektiren senaryoları tek bir pencere fonksiyonuyla daha okunabilir ve performanslı hale getirme.
SQL Pencere Fonksiyonları, veri analistleri ve geliştiriciler için güçlü ve esnek bir araç setidir. Geleneksel aggregate fonksiyonlarının veya GROUP BY ifadesinin yetersiz kaldığı durumlarda, veriye derinlemesine inerek karmaşık iş mantıklarını çözümlemede önemli bir rol oynarlar. Bu fonksiyonları etkin bir şekilde kullanmak, daha anlamlı raporlar oluşturmanızı, performansı artırmanızı ve SQL sorgularınızı daha okunabilir hale getirmenizi sağlayacaktır.