0 212 951 05 08   bilgi@ofisdata.com

Yazılarımız

OfisData

SQL İLE RAPORLAMA: GROUP BY, AGGREGATE FONKSİYONLAR VE İŞ SENARYOLARI

Raporlama ihtiyacı ortaya çıktığında ilk refleks çoğu zaman “veriyi çekelim, Excel’de toparlarız” olur. Oysa SQL, doğru kurgulandığında hem metrikleri güvenilir biçimde üretir hem de raporların sürdürülebilir olmasını sağlar. Bu noktada GROUP BY ve aggregate fonksiyonlar (SUM, COUNT, AVG, MIN, MAX) kurumsal raporlamanın temel taşıdır.

Bu makalede “kaç sattık?” gibi basit sorulardan, “hangi segmentte kârlılık düşüyor?” gibi karar destek sorularına kadar uzanan bir çizgide, toplulaştırma mantığını iş senaryoları üzerinden ele alacağız. Ayrıca HAVING ile iş kurallarını nasıl güvenli uygulayacağınızı, koşullu toplulaştırmayı nasıl kuracağınızı ve performans tarafında nelere dikkat edeceğinizi göreceksiniz.

Hedefimiz sadece sentaks öğretmek değil; doğru ölçüm, tutarlı tanım ve güvenilir sonuç üretme pratiğini kazanmanız. Kurum içinde raporların birbiriyle çelişmesi çoğu zaman SQL’in “yanlış” olmasından değil, metrik tanımının belirsiz olmasından ve sorgu yaklaşımının tutarsızlığından kaynaklanır. Buradaki yaklaşımı standartlaştırdığınızda, ekip içinde rapor güveni belirgin biçimde yükselir.

Bir veri analisti ekranında satış verilerini kolonlara göre özetleyen SQL sorgusu ve tablo sonuçları

Primary keyword: SQL ile raporlama yaklaşımında GROUP BY mantığı

SQL ile raporlama yaparken ilk kritik karar “hangi boyutta raporluyorum?” sorusudur. GROUP BY, seçtiğiniz boyutlara göre veriyi kümeler ve her küme için aggregate değerler üretir. Bir başka deyişle, satır seviyesindeki veriyi “özet tablo”ya çevirir.

Basit bir kural: SELECT kısmında aggregate olmayan her kolon, GROUP BY içinde yer almalıdır. Bu kural, raporun boyutlarını netleştirir. Boyutlar arttıkça (örn. ülke + şehir + kanal + ürün), rapor granülerleşir; rapor satır sayısı artar ve yorumlama farklılaşır.

Gruplama anahtarları: boyut seçimi ve granülerlik

Kurumsal raporlama çoğu zaman bir hiyerarşi üzerine kurulur: bölge → mağaza → ürün kategorisi → ürün gibi. Bu hiyerarşiyi SQL tarafında doğru temsil etmek, raporun “aynı şeyi ölçüyor” olmasını sağlar. Boyutları gereksiz artırmak, raporu okunmaz kılabilir; eksik bırakmak ise ayrıntıyı kaybettirir.

  • Yönetici özeti için: tarih (ay/hafta) + kanal gibi daha düşük granülerlik
  • Operasyon için: gün + mağaza + ürün gibi daha yüksek granülerlik
  • Finans için: dönem + maliyet merkezi + hesap planı gibi muhasebe boyutları

Burada önemli nokta, her raporun “tek bir granülerlik” ile tasarlanmasıdır. Aynı raporda hem ürün hem sipariş satırı seviyesini karıştırmak, çift sayma riskini artırır.

Tarih bazlı raporlama: gün, hafta, ay kırılımları

Raporların büyük kısmı zaman boyutludur. Tarih kırılımını doğru belirlemek, trend analizini doğrudan etkiler. Örneğin günlük raporda ay sonu etkileri, kampanya günleri ve stok kırılmaları görünür; aylık raporda ise daha stabil bir eğilim elde edilir.

Tarih alanını doğrudan GROUP BY’a koymak çoğu zaman günlük rapor üretir. Haftalık veya aylık rapor için tarih alanını dönüştürmek gerekir (kullandığınız veritabanına göre DATE_TRUNC, FORMAT, EXTRACT gibi fonksiyonlar değişebilir). Burada amaç “aynı döneme ait kayıtları tek grupta toplamak”tır.

Aggregate fonksiyonlar: doğru metrik üretimi ve yaygın tuzaklar

Aggregate fonksiyonlar, raporun metrik katmanını oluşturur. Ancak metrik üretimi sadece SUM yazmaktan ibaret değildir; NULL davranışı, filtreleme sırası ve veri modeli, sonucu dramatik biçimde değiştirebilir. Bu nedenle rapor metriklerini tanımlarken “hangi kayıtlar dahil?” sorusunu netleştirin.

Örneğin “aktif müşteri sayısı” farklı ekiplerde farklı ölçülebilir: son 30 günde işlem yapan müşteri mi, üyeliği aktif olan müşteri mi, yoksa faturası kesilen müşteri mi? SQL doğru çalışsa bile tanım net değilse raporlar çelişir.

COUNT, SUM, AVG: NULL ve DISTINCT etkisi

COUNT(*) satır sayısını verir; COUNT(kolon) ise NULL olmayan değerleri sayar. Bu fark, özellikle veri kalitesi sorunlarında beklenmedik sapmalar doğurur. SUM ve AVG de NULL değerleri görmezden gelir; bu iyi bir özellik olabilir ama “eksik veri”yi saklamasına da neden olabilir.

DISTINCT kullanımı da dikkat ister. COUNT(DISTINCT musteri_id) “benzersiz müşteri”yi sayar; ancak aynı müşterinin farklı segmentlerde tekrar etmesi gibi bir durum varsa, segment bazlı raporlarda farklı sonuçlar üretebilir. Bu nedenle “hangi seviyede benzersizlik?” sorusu rapor tasarımının merkezinde olmalıdır.

Koşullu toplulaştırma: tek sorguda çok KPI

Bir raporda birden fazla KPI üretmek sık rastlanan bir ihtiyaçtır: toplam ciro, iade cirosu, net ciro, sipariş adedi, aktif müşteri gibi. Bunu farklı sorgularla birleştirmek yerine, koşullu toplulaştırma ile tek sorguda üretmek hem daha okunur hem de daha az hata eğilimlidir.

-- Örnek: Kanal bazında net ciro ve iade oranı (genel SQL yaklaşımı)
SELECT
  kanal,
  COUNT(*) AS siparis_adedi,
  COUNT(DISTINCT musteri_id) AS benzersiz_musteri,
  SUM(tutar) AS brut_ciro,
  SUM(CASE WHEN is_iade = 1 THEN tutar ELSE 0 END) AS iade_ciro,
  SUM(CASE WHEN is_iade = 0 THEN tutar ELSE 0 END) AS satis_ciro,
  -- Net ciro: satış - iade
  SUM(CASE WHEN is_iade = 0 THEN tutar ELSE 0 END) - SUM(CASE WHEN is_iade = 1 THEN tutar ELSE 0 END) AS net_ciro
FROM siparisler
WHERE siparis_tarihi >= '2026-01-01'
  AND siparis_tarihi <  '2026-02-01'
GROUP BY kanal
ORDER BY net_ciro DESC;

Bu yaklaşımda en kritik nokta, koşulların iş tanımıyla uyumlu olmasıdır. “İade”yi nasıl etiketliyorsunuz? Kısmi iade var mı? İade satırı farklı tabloda mı tutuluyor? Bu soruları netleştirmeden KPI üretmek, raporu “güzel ama yanlış” hale getirebilir.

HAVING ile filtreleme: iş kurallarını güvenle uygulamak

WHERE satır seviyesinde filtreler; HAVING ise toplulaştırma sonucunu filtreler. Kurumsal raporlama senaryolarında “toplam satış 100.000’i aşan mağazalar” veya “en az 50 sipariş alan ürünler” gibi koşullar, HAVING ile daha doğru ifade edilir.

WHERE ve HAVING farkı: aynı cümle, farklı sonuç

“İade oranı %10’dan yüksek kanallar” gibi bir cümlede oran, aggregate ile üretildiği için HAVING tarafına aittir. WHERE’a yazmaya çalışmak çoğu veritabanında hata verir; bazı durumlarda ise yanlış kurguyla “önce filtrele, sonra hesapla” etkisi yaratıp sonucu çarpıtabilir.

Pratik bir kontrol: Eğer koşul, SUM/COUNT/AVG gibi bir aggregate sonucu gerektiriyorsa HAVING; kayıt düzeyinde bir alanı koşullandırıyorsa WHERE kullanın.

KPI eşikleri: karar verici diliyle raporlama

Kurumsal raporlarda sonuçların “karar aldıran” bir formatta sunulması beklenir. Bu yüzden raporun bir bölümü çoğu zaman eşiklere göre filtrelenir: düşük performanslı mağazalar, kârlılığı azalan ürünler, SLA’i bozan ekipler gibi. HAVING, bu eşikleri veritabanı seviyesinde uygulayarak rapor çıktısını küçültür ve odaklanmayı artırır.

Büyük bir müşteri tablosunda şehir bazlı gruplama ve toplam ciro metriklerini karşılaştıran rapor çıktısı

İş senaryoları: satış, finans ve operasyon raporları

SQL ile raporlama yaparken en büyük kazanım, tekrar eden soruları standart sorgu şablonlarına dönüştürmektir. Aşağıdaki senaryolar, kurumlarda sık görülen ve GROUP BY ile doğrudan çözülen örneklerdir. Her birinde önce “boyut” (neye göre gruplayacağız), sonra “metrik” (neyi ölçeceğiz) yaklaşımını izleyin.

Satış performansı: ürün ve kanal bazında trend

Satış ekipleri genelde ürün, kategori, bölge ve kanal kırılımını aynı anda görmek ister. Burada iki kritik nokta vardır: (1) ürün hiyerarşisini doğru bağlamak, (2) zaman kırılımını rapor amacıyla seçmek. Haftalık trend, kampanyaları okumak için çoğu zaman daha uygundur; aylık trend ise bütçe takibinde öne çıkar.

  1. Önce metrikleri tanımlayın: net ciro mu, brüt ciro mu?
  2. Sonra boyutları sabitleyin: ay + kanal + kategori gibi.
  3. En son eşikleri uygulayın: belirli ciro altını hariç tutma vb.

Finans raporları: dönemsel toplamlar ve sapma analizi

Finans raporlarında “dönem kapanış” mantığı önemlidir. Muhasebe kayıtlarının geriye dönük güncellenmesi, raporların da yeniden hesaplanmasını gerektirebilir. Bu nedenle sorgularda dönem filtresi (örn. 2026-01) net olmalı ve mümkünse bir takvim tablosu üzerinden raporlanmalıdır.

Sapma analizi için iki dönem karşılaştırması yapılır: cari dönem ve önceki dönem. Burada GROUP BY ile dönem bazlı toplulaştırma yapıp, sonuçları birleştirmek (self join veya CTE) raporu daha düzenli kılar.

Operasyon raporları: SLA, işlem süreleri ve kuyruklar

Operasyon tarafında raporlar genelde “adet” ve “süre” odaklıdır: kaç talep geldi, kaç talep çözüldü, ortalama çözüm süresi nedir, SLA aşımları nerede yoğunlaştı? AVG ile süre metriği üretirken outlier etkisini düşünün; medyan gerekiyorsa veritabanı fonksiyonlarını veya ek bir analiz katmanını değerlendirin.

-- Örnek: Mağaza bazında yüksek hacimli ürünleri seçmek (HAVING ile)
WITH urun_satis AS (
  SELECT
    magaza_id,
    urun_id,
    SUM(adet) AS toplam_adet,
    SUM(tutar) AS toplam_tutar
  FROM satis_satirlari
  WHERE satis_tarihi >= '2026-01-01'
    AND satis_tarihi <  '2026-02-01'
  GROUP BY magaza_id, urun_id
)
SELECT
  magaza_id,
  urun_id,
  toplam_adet,
  toplam_tutar
FROM urun_satis
WHERE toplam_tutar >= 50000
ORDER BY toplam_tutar DESC;

Bu örnek, önce temel toplulaştırmayı yapıp sonra filtrelemeyi ikinci aşamada uygular. Büyük veri setlerinde bu iki adımlı yaklaşım, sorguyu daha okunur hale getirirken optimize etmeyi de kolaylaştırır. Ayrıca CTE, iş birimleriyle konuşurken “ara tablo” mantığını açıklamayı pratikleştirir.

Veri kalitesi ve tutarlılık: raporların güvenilir olması için kontroller

Kurumsal raporlamada en pahalı problem, hatalı metrik üretimidir. Hatalı rapor, yanlış karar üretir. Bu nedenle rapor sorgularında küçük kalite kontrolleri alışkanlık haline gelmelidir. Özellikle kaynak sistemlerden gelen NULL değerler, beklenmeyen negatif tutarlar ve mükerrer kayıtlar raporu bozar.

Çift sayma ve join kaynaklı şişme

GROUP BY hatalarının önemli bir kısmı join’lerden gelir. Örneğin sipariş başlığı (header) ile satır (line) tablosunu birleştirip, sonra sipariş başlığı seviyesinde metrik üretmeye çalışırsanız, satır sayısı kadar çoğalma olabilir. Bu nedenle rapor granülerliğini belirledikten sonra join’leri o granülerliğe uygun kurun.

Kontrol sorguları: toplamların uzlaştırılması

Rapor çıktısını üretmeden önce iki basit kontrol faydalıdır: (1) aynı dönem için toplam ciroyu tek metrikle çekip rapor toplamıyla karşılaştırmak, (2) filtre uygulanmış ve uygulanmamış sonuçları kıyaslamak. Bu pratik, üretim ortamına taşınan raporlarda güveni artırır ve regresyon hatalarını azaltır.

Performans optimizasyonu: büyük tablolarla raporlama

Raporlama sorguları genelde “okuma ağırlıklı”dır ve büyük veri üzerinde çalışır. Bu nedenle performans, kullanıcı deneyimi kadar maliyeti de etkiler. İlk optimizasyon adımı, WHERE filtrelerinin seçici olmasını sağlamak ve doğru indeksleri kullanmaktır. İkinci adım ise gereksiz kolonları taşımamak ve doğru toplulaştırma stratejisini seçmektir.

İndeks, filtreleme ve kapsayıcı alanlar

Sık raporlanan alanlar (tarih, kanal, mağaza, kategori) üzerinde indeks stratejisi belirlemek tipik bir kazanım sağlar. Ancak indeks her derde deva değildir; çok fazla indeks yazma maliyetini artırabilir. Burada amaç, raporların temel filtreleri ve join anahtarları için dengeli bir yapı kurmaktır.

Bir diğer pratik: raporda kullanmadığınız alanları SELECT’e koymayın. Özellikle büyük metin alanları veya JSON kolonları, gereksiz I/O üretir. Bu basit disiplin, beklenenden fazla hızlandırma sağlayabilir.

Bölümleme ve özet tablolar: sürdürülebilir raporlama

Veri büyüdükçe tek bir ham tablo üzerinden raporlama zorlaşır. Dönemsel bölümleme (partitioning) veya günlük/haftalık özet tablolar, rapor yükünü düşürür. Bu yaklaşım özellikle “her sabah aynı raporu çalıştırıyoruz” gibi rutinlerde etkilidir. Özet tabloların metrik tanımları merkezi bir yerde tutulmalı ve versiyonlanmalıdır.

Veri ambarında indeks ve sorgu planı bilgisiyle hızlandırılmış toplulaştırma raporu senaryosu

Modern raporlama: GROUP BY ile pencere fonksiyonlarını birlikte kullanmak

GROUP BY özet üretir; pencere fonksiyonları ise aynı satırda hem detay hem de bağlamsal metrik görmeyi sağlar. Örneğin “her mağazanın toplamı ve bu toplamın bölge içindeki payı” gibi ihtiyaçlarda, önce toplulaştırma sonra pencere fonksiyonu yaklaşımı oldukça etkilidir.

Pay hesapları ve sıralama: aynı çıktıda kıyaslama

Bir yönetici raporunda sadece toplamlar yetmez; toplamların karşılaştırmalı olarak sıralanması, yüzdelerin hesaplanması ve ilk N’in seçilmesi istenir. Bu işlerde pencere fonksiyonları raporu daha az katmanla üretmenizi sağlar. Yine de temel prensip değişmez: önce doğru metrik, sonra doğru sunum.

Eğitim ve standardizasyon: ekip içinde ortak raporlama dili

Kurumsal ekiplerde en hızlı ilerleme, ortak bir sorgu standardı ve metrik sözlüğü ile olur. GROUP BY ve aggregate fonksiyonları doğru kullanan bir ekip, raporların bakımını kolaylaştırır ve “aynı soruya farklı cevap” problemini azaltır. Eğer ekip içinde SQL olgunluğunu artırmak istiyorsanız, kurumsal ihtiyaçlara göre kurgulanmış bir eğitim programı etkili olur.

SQL pratiklerini daha sistemli ilerletmek için SQL Eğitimi sayfasındaki modüller, raporlama odağında güçlü bir çerçeve sunabilir. Burada amaç, sadece sorgu yazmak değil; tanımı net, test edilebilir ve performanslı raporlar üretmeyi alışkanlık haline getirmektir.


Özetle: SQL ile raporlama, “GROUP BY yazıp geçmek” değildir. Boyut seçimi, metrik tanımı, HAVING ile iş kuralı, veri kalitesi kontrolleri ve performans optimizasyonu bir bütün olarak ele alındığında, raporlar karar destek sistemine dönüşür. Bu makaledeki örnekleri kendi veri modelinize uyarlarken, önce tanımı netleştirin; sonra sorgu tasarımını adım adım ilerletin.

 OFİS DATA