SQL GROUP BY RAPORLAMA

SQL veritabanı silindiri yanında satırların gruplara toplanıp özet rapora dönüşmesi

Patron mesaj atıyor: "Şehir bazında bu yılın ciro dökümünü görebilir miyim?" Elinizde 300.000 satırlık bir sipariş tablosu var. Satırları Excel'e atıp saatlerce uğraşmak bir yol; doğru yol ise veritabanına tek soru sormak. O sorunun adı GROUP BY ve yönetim raporlarının ezici çoğunluğu — şehir bazında ciro, ay bazında sipariş adedi, temsilci bazında ortalama sepet — bu tek yapının üzerine kurulur.

Mantık iki adımdır: önce satırları ortak değere göre kümele (aynı şehirdekiler bir araya), sonra her kümeyi tek satıra indir (topla, say, ortalama al). Aşağıdaki örneklerin tamamı tipik bir siparis tablosu üzerinden ilerliyor: id, tarih, sehir, temsilci, tutar.

Temel Kalıp: Grupla ve Topla

SELECT sehir,
       COUNT(*)   AS siparis_adedi,
       SUM(tutar) AS toplam_ciro,
       AVG(tutar) AS ortalama_sepet
FROM siparis
WHERE tarih >= '2026-01-01'
GROUP BY sehir
ORDER BY toplam_ciro DESC;

Okunuşu: 2026 siparişlerini al, şehre göre kümele, her şehir için adet/toplam/ortalama hesapla, ciroya göre büyükten küçüğe sırala. Çıktı artık satır listesi değil rapordur — şehir başına tek satır.

Tek yazım kuralı her şeyi belirler: SELECT'te ya gruplanan kolon ya toplulaştırma fonksiyonu olur. Gruplamadığınız bir kolonu (örn. temsilci) çıplak yazarsanız çoğu veritabanı hata verir — çünkü "İstanbul kümesinin temsilcisi kim?" sorusunun tek cevabı yoktur.

Toplulaştırma Fonksiyonları

  • COUNT(*) — gruptaki satır sayısı; COUNT(kolon) yalnız dolu (NULL olmayan) değerleri sayar
  • COUNT(DISTINCT musteri_id) — tekil müşteri sayısı; "kaç farklı müşteri?" sorusunun cevabı
  • SUM / AVG — toplam ve ortalama (AVG, NULL'ları hesaba katmaz)
  • MIN / MAX — en küçük/en büyük; tarih kolonunda "ilk ve son sipariş" anlamına gelir

WHERE ve HAVING: En Çok Karıştırılan Çift

WHERE satır filtresi ve HAVING grup filtresi arasındaki sıralama farkı şeması

İkisi de filtredir; fark ne zaman çalıştıklarıdır. WHERE gruplamadan önce satırları eler; HAVING gruplamadan sonra grup sonuçlarını eler:

SELECT sehir, SUM(tutar) AS toplam_ciro
FROM siparis
WHERE tarih >= '2026-01-01'      -- önce: sadece 2026 satırları
GROUP BY sehir
HAVING SUM(tutar) > 1000000      -- sonra: cirosu 1M'yi aşan şehirler
ORDER BY toplam_ciro DESC;

Ezber cümlesi: "satır şartı WHERE'e, toplam şartı HAVING'e." WHERE SUM(tutar) > ... yazmak sözdizimi hatasıdır — henüz toplanacak grup yokken toplam filtrelenemez.

Aylık Rapor: Tarihe Göre Gruplama

Zaman bazlı özet, raporlamanın ekmek kapısıdır. Tarihi ay hassasiyetine indirip gruplarsınız:

-- SQL Server
SELECT YEAR(tarih) AS yil, MONTH(tarih) AS ay,
       SUM(tutar)  AS aylik_ciro
FROM siparis
GROUP BY YEAR(tarih), MONTH(tarih)
ORDER BY yil, ay;

-- PostgreSQL eşdeğeri: GROUP BY DATE_TRUNC('month', tarih)
-- MySQL eşdeğeri:      GROUP BY DATE_FORMAT(tarih, '%Y-%m')

Lehçe farkı yalnızca tarih fonksiyonundadır; gruplama mantığı her motorda aynıdır. Çok boyutlu rapor için kolonları yan yana eklersiniz: GROUP BY YEAR(tarih), MONTH(tarih), sehir — ay × şehir kırılımı.

ROLLUP: Ara Toplamları Veritabanına Yaptırmak

Klasik ihtiyaç: şehir bazında döküm + en altta genel toplam. Bunu uygulama tarafında toplamak yerine tek kelimeyle SQL'e yaptırırsınız:

SELECT sehir, SUM(tutar) AS toplam
FROM siparis
GROUP BY ROLLUP(sehir);

Çıktının son satırında sehir = NULL olan genel toplam satırı belirir. İki kolonlu ROLLUP(sehir, temsilci) hem şehir ara toplamlarını hem genel toplamı üretir — Excel'deki alt toplam görünümünün veritabanı karşılığı. Rapor katmanında NULL'u "TOPLAM" etiketine çevirmek için COALESCE(sehir, 'TOPLAM') kalıbı kullanılır.

Gerçek Rapor Tarifleri

Aylık ciro temsilci performansı ve tekil müşteri raport şablonları kart düzeni

Temsilci performans tablosu:

SELECT temsilci,
       COUNT(*)                  AS siparis,
       COUNT(DISTINCT musteri_id) AS tekil_musteri,
       SUM(tutar)                AS ciro,
       MAX(tutar)                AS en_buyuk_satis
FROM siparis
WHERE tarih >= '2026-01-01'
GROUP BY temsilci
ORDER BY ciro DESC;

Birden çok kez sipariş veren müşteriler (sadakat listesi):

SELECT musteri_id, COUNT(*) AS siparis_sayisi
FROM siparis
GROUP BY musteri_id
HAVING COUNT(*) >= 3
ORDER BY siparis_sayisi DESC;

Tekrarlanan kayıt avı (veri kalitesi):

SELECT email, COUNT(*) AS adet
FROM musteri
GROUP BY email
HAVING COUNT(*) > 1;

Son örnek GROUP BY'ın az bilinen ama çok kullanılan yüzüdür: raporlama dışında mükerrer kayıt tespiti de aynı kalıpla yapılır.

Sık Yapılan Üç Hata

  1. Gruplanmayan çıplak kolon: SELECT'e GROUP BY'da olmayan kolon yazmak. Çözüm: kolonu ya GROUP BY'a ekleyin ya toplulaştırmaya sarın.
  2. WHERE'de toplulaştırma: WHERE COUNT(*) > 5 çalışmaz; grup şartları HAVING'e yazılır.
  3. NULL körlüğü: AVG NULL'ları saymaz, COUNT(kolon) NULL'ları atlar; "ortalama neden beklediğimden yüksek?" sorusunun cevabı çoğu zaman budur. Gerekirse COALESCE(tutar, 0) ile NULL'u sıfıra çevirin.

Sözdizimi başvurusu için Microsoft'un GROUP BY belgeleri ROLLUP/CUBE dahil tüm varyantları örnekli anlatır.

Sonraki Adım

GROUP BY rapor sorgularının iskeletidir; üzerine JOIN (müşteri adını siparişe bağlamak), alt sorgular ve pencere fonksiyonları (grup içi sıralama, kümülatif toplam) eklendiğinde tam bir raporlama dili elde edersiniz. Bu yolu sistemli yürümek isteyenler için SQL eğitimi SELECT'ten raporlama desenlerine uzanan uygulamalı bir program sunar; sonuçları Excel/Power BI katmanında görselleştirme tarafı ise veri analizi ve raporlama eğitiminde derinleşir.

Patronun sorusuna dönelim: "şehir bazında ciro dökümü" artık 300.000 satırlık dert değil — yedi satırlık bir sorgu. GROUP BY'ı öğrenmek, veritabanına liste soran kişiden rapor soran kişiye dönüşmektir.

Sıkça Sorulan Sorular

SQL'de GROUP BY ne işe yarar?

Satırları ortak değere göre kümeleyip her kümeyi tek özet satıra indirir: şehir bazında toplam ciro, ay bazında sipariş adedi, temsilci bazında ortalama satış gibi. COUNT, SUM, AVG, MIN, MAX fonksiyonlarıyla birlikte çalışır ve yönetim raporlarının büyük bölümünün SQL karşılığıdır.

WHERE ile HAVING arasındaki fark nedir?

WHERE gruplamadan önce satırları filtreler, HAVING gruplamadan sonra grup sonuçlarını filtreler. 'Sadece 2026 siparişleri' WHERE'in işidir; 'cirosu 1 milyonu aşan şehirler' HAVING'in. Toplulaştırma içeren şart (SUM, COUNT...) WHERE'e yazılamaz — henüz hesaplanmamış toplam filtrelenemez.

GROUP BY kullanırken neden 'kolon geçersiz' hatası alıyorum?

SELECT listesine GROUP BY'da olmayan ve toplulaştırmaya sarılmamış bir kolon yazdığınız için. Kural nettir: SELECT'teki her kolon ya GROUP BY'da yer alır ya da COUNT/SUM/AVG gibi bir fonksiyonun içindedir. Grupta tek değeri olmayan kolonun 'hangi değeri gösterilsin?' sorusu cevapsızdır; motor bu yüzden reddeder.

Aylık ciro raporunu GROUP BY ile nasıl alırım?

Tarihi ay hassasiyetine indirip gruplayın: SQL Server'da GROUP BY YEAR(tarih), MONTH(tarih); PostgreSQL'de DATE_TRUNC('month', tarih); MySQL'de DATE_FORMAT(tarih, '%Y-%m'). SELECT'te aynı ifadeler + SUM(tutar) yer alır. Lehçeler yalnızca tarih fonksiyonunda ayrışır; gruplama mantığı tüm motorlarda aynıdır.

ROLLUP ne işe yarar, ne zaman kullanılır?

GROUP BY ROLLUP(kolon), normal grup satırlarına ek olarak ara toplam ve genel toplam satırları üretir — Excel'deki alt toplam görünümünün SQL karşılığı. Toplam satırlarında gruplanan kolon NULL gelir; raporda COALESCE(kolon, 'TOPLAM') ile etiketlenir. Döküm + toplam isteyen her yönetim raporunda uygulama tarafını ek hesaptan kurtarır.

COUNT(*) ile COUNT(kolon) arasında fark var mı?

Var ve raporu sessizce değiştirir: COUNT(*) gruptaki tüm satırları sayar; COUNT(kolon) yalnız o kolonu dolu (NULL olmayan) satırları sayar; COUNT(DISTINCT kolon) tekil değerleri sayar. 'Sipariş adedi' için COUNT(*), 'kaç farklı müşteri' için COUNT(DISTINCT musteri_id) doğru tercihtir.

GROUP BY sorgum yavaş, nasıl hızlandırırım?

İlk bakılacak yer filtre ve gruplama kolonlarının indeksidir: WHERE'deki tarih ve GROUP BY'daki kolon indeksliyse motor taramayı ciddi kısaltır. İkinci kazanç WHERE ile veri kümesini gruplamadan önce küçültmektir. Büyük tablolarda yürütme planına (EXPLAIN) bakmak, eksik indeksi tahminle değil kanıtla bulmanın yoludur.