MySQL 8.0, pencere fonksiyonlarını (window functions) yerleşik olarak sunar. Bu rehber; sürüm kapsamını, temel sözdizimini, gerçek sorgu kalıplarını ve sık yapılan hataları orta seviye geliştiriciler için adım adım ele alır.
Önkoşullar ve Sürüm Kapsamı
- MySQL 8.0 veya üzeri bir sunucu gerekir. Pencere fonksiyonları 8.0 öncesi sürümlerde (5.7 dahil) desteklenmez; bu sürümlerde
OVERsözdizimi hata verir. - MariaDB farklı bir ürün ve sürüm çizgisine sahiptir; burada yalnızca MySQL 8.0 davranışı esas alınır.
- Örnekler
ORDER BYveGROUP BYile temel düzeyde rahat olan bir okuyucuyu hedefler.
Pencere Fonksiyonu Nedir?
Pencere fonksiyonu, satırları tek bir çıktı satırına indirgemeden, her satır için bir "pencere" (satır kümesi) üzerinden hesaplama yapar. GROUP BY'dan temel farkı budur: gruplama satırları birleştirirken, pencere fonksiyonu satırları korur ve yanlarına hesaplanmış bir değer ekler.
Sözdiziminin çekirdeği şudur:
fonksiyon(...) OVER (
[PARTITION BY ...]
[ORDER BY ...]
[frame_clause]
)
PARTITION BY: Satırları bağımsız gruplara ayırır. Belirtilmezse tüm sonuç kümesi tek bir pencere olur.ORDER BY: Pencere içindeki satır sırasını belirler; sıralamaya bağlı fonksiyonlar için kritiktir.frame_clause: Hesaplamanın hangi satır aralığında yapılacağını tanımlar (örneğinROWS BETWEEN ... AND ...).
Adım 1: Örnek Tablo ve Veri
Aşağıdaki tabloyu ve verileri kendi ortamınızda oluşturup deneyebilirsiniz:
CREATE TABLE satislar (
id INT PRIMARY KEY,
bolge VARCHAR(20) NOT NULL,
temsilci VARCHAR(50) NOT NULL,
ay DATE NOT NULL,
tutar DECIMAL(10,2) NOT NULL
);
INSERT INTO satislar (id, bolge, temsilci, ay, tutar) VALUES
(1, 'Ege', 'Ayşe', '2024-01-01', 1000.00),
(2, 'Ege', 'Ayşe', '2024-02-01', 1500.00),
(3, 'Ege', 'Mehmet','2024-01-01', 800.00),
(4, 'Ege', 'Mehmet','2024-02-01', 1200.00),
(5, 'Marmara','Zeynep','2024-01-01', 2000.00),
(6, 'Marmara','Zeynep','2024-02-01', 1800.00);
Adım 2: Sıralama Fonksiyonları
Sıralama fonksiyonları bir pencere içindeki satırlara sıra numarası atar. En sık kullanılan üçü:
ROW_NUMBER(): Eşit değerlerde bile her satıra benzersiz, artan bir numara verir.RANK(): Eşit değerlere aynı sırayı verir; sonraki sırada boşluk bırakır (1, 1, 3...).DENSE_RANK(): Eşit değerlere aynı sırayı verir; boşluk bırakmaz (1, 1, 2...).
SELECT
bolge,
temsilci,
ay,
tutar,
ROW_NUMBER() OVER (PARTITION BY bolge ORDER BY tutar DESC) AS sira,
RANK() OVER (PARTITION BY bolge ORDER BY tutar DESC) AS rnk,
DENSE_RANK() OVER (PARTITION BY bolge ORDER BY tutar DESC) AS yogun_rnk
FROM satislar
ORDER BY bolge, tutar DESC;
Beklenen davranış: Her bölge kendi içinde ayrı numaralanır. Aynı tutara sahip iki satır varsa ROW_NUMBER onlara farklı numara verirken, RANK ve DENSE_RANK aynı numarayı verir; RANK sonraki numarada atlama yapar, DENSE_RANK yapmaz.
Adım 3: Toplulaştırma Fonksiyonlarını Pencere Olarak Kullanma
SUM, AVG, COUNT, MIN, MAX gibi toplulaştırma fonksiyonları OVER ile birlikte kullanıldığında satırları korur. Bu, "her satırın yanında grubun toplamı" gibi bir çıktı üretir.
SELECT
bolge,
temsilci,
ay,
tutar,
SUM(tutar) OVER (PARTITION BY bolge) AS bolge_toplam,
ROUND(tutar / SUM(tutar) OVER (PARTITION BY bolge) * 100, 2) AS bolge_payi_yuzde
FROM satislar
ORDER BY bolge, tutar DESC;
Beklenen davranış: bolge_toplam her bölge için sabit bir değerdir; bolge_payi_yuzde ise o satırın bölge toplamına oranını verir. Satır sayısı değişmez.
Adım 4: Kümülatif Toplam ve Pencere Çerçevesi
Varsayılan çerçeve, ORDER BY varlığında "başlangıçtan geçerli satıra kadar" anlamına gelir. Bu, kümülatif (running) toplam için doğrudan kullanılabilir:
SELECT
bolge,
temsilci,
ay,
tutar,
SUM(tutar) OVER (
PARTITION BY bolge, temsilci
ORDER BY ay
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS kumulatif_tutar
FROM satislar
ORDER BY bolge, temsilci, ay;
Beklenen davranış: Her temsilci için ay bazında artan bir kümülatif toplam üretilir. ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ifadesi açıkça yazıldığında, varsayılan çerçeveye bağımlı kalmazsınız.
Çerçeve seçimi önemlidir: ROWS fiziksel satır sayısına göre çalışır; RANGE ise ORDER BY sütunundaki değer eşitliğine göre çalışır. Eşit değerlerin bulunduğu verilerde RANGE beklenmedik toplamlar üretebilir; bu yüzden niyetinizi ROWS ile netleştirmek çoğu durumda daha güvenlidir.
Adım 5: Önceki/Sonraki Satıra Erişim
LAG ve LEAD, geçerli satıra göre önceki veya sonraki satırın değerini getirir. Fark ve değişim hesaplarında kullanışlıdır.
SELECT
bolge,
temsilci,
ay,
tutar,
LAG(tutar) OVER (PARTITION BY bolge, temsilci ORDER BY ay) AS onceki_tutar,
tutar - LAG(tutar) OVER (PARTITION BY bolge, temsilci ORDER BY ay) AS degisim
FROM satislar
ORDER BY bolge, temsilci, ay;
Beklenen davranış: Her temsilcinin ilk ayında onceki_tutar ve degisim NULL olur; sonraki aylarda bir önceki aya göre fark hesaplanır. LAG/LEAD için isteğe bağlı bir ofset ve varsayılan değer de verilebilir (örneğin LAG(tutar, 1, 0)), ancak varsayılanın NULL olduğunu unutmayın.
Adım 6: Grup İçinde İlk/Son Satırı Seçme
"Her grubun en yüksek tutarlı satırını getir" gibi sorular için yaygın kalıp, pencere fonksiyonunu bir alt sorguda kullanıp dışarıda filtrelemektir:
SELECT bolge, temsilci, ay, tutar
FROM (
SELECT
bolge, temsilci, ay, tutar,
ROW_NUMBER() OVER (PARTITION BY bolge ORDER BY tutar DESC) AS sira
FROM satislar
) AS t
WHERE sira = 1;
Beklenen davranış: Her bölge için en yüksek tutarlı tek satır döner. Bu kalıp, GROUP BY ile "en yüksek tutar"ı bulup sonra eşleştirmekten genellikle daha okunaklıdır.
Sık Yapılan Hatalar
- Pencere fonksiyonunu
WHEREiçinde kullanmak. Pencere fonksiyonlarıWHEREaşamasından sonra hesaplanır; bu nedenle doğrudanWHERE sira = 1yazılamaz. Çözüm: alt sorgu veyaWITH(CTE) kullanıp dış katmanda filtrelemek. ORDER BY'ı unutmak.ROW_NUMBER,RANK,LAG,LEADgibi fonksiyonlar sıralamaya bağlıdır;ORDER BYverilmezse sonuç belirsizdir.PARTITION BYileGROUP BY'ı karıştırmak. Pencere fonksiyonu satırları birleştirmez; gruplama bekliyorsanızGROUP BYgerekir.- Çerçeve türünü yanlış seçmek. Eşit
ORDER BYdeğerlerindeRANGEveROWSfarklı sonuç verir. Niyetinizi açıkça belirtin. - Aynı sorguda birden çok farklı pencere tanımlamak. Her
OVERbloğu ayrı bir pencere tanımıdır; sık tekrarlanan tanımlarıWINDOWyan tümcesiyle adlandırarak okunabilirliği artırabilirsiniz.
Performans ve Güvenlik Notları
- Pencere fonksiyonları genellikle sıralama ve ara sonuç gerektirir.
PARTITION BYveORDER BYsütunlarındaki indeksler, uygun olduğunda planı iyileştirebilir; ancak her sorgu için indeks kullanımı garanti değildir. Gerçek planıEXPLAINile doğrulayın. - Pencere fonksiyonları büyük sonuç kümelerinde bellek ve geçici tablo kullanımını artırabilir. Dar bir
WHEREfiltresiyle önce veriyi küçültmek çoğu durumda faydalıdır. - Güvenlik açısından pencere fonksiyonları SQL enjeksiyonuna karşı kendiliğinden koruma sağlamaz. Kullanıcı girdisi sorguya parametre olarak bağlanmalı;
PARTITION BY/ORDER BYgibi yapısal parçalar asla doğrudan dizge birleştirmeyle eklenmemelidir. - Pencere fonksiyonları, uygulama katmanındaki yetkilendirme mantığının yerini tutmaz. Satır düzeyinde erişim kontrolü gerekiyorsa bunu ayrıca uygulayın.
Özet
MySQL 8.0 pencere fonksiyonları; sıralama, kümülatif toplam, önceki/sonraki satır erişimi ve grup içi seçim gibi işleri tek sorguda, satırları koruyarak çözmenizi sağlar. Doğru sonuç için PARTITION BY, ORDER BY ve çerçeve tanımını bilinçli seçin; pencere fonksiyonlarını filtrelemek için alt sorgu veya CTE kullanın. Sürüm kapsamını ve plan davranışını EXPLAIN ile doğrulamak, üretim sorgularında beklenmedik sonuçların önüne geçer.