← Kursa Dön
📄 Text · 35 min

GROUP BY Temelleri

Giriş — Verileri Gruplama

Önceki derste aggregate fonksiyonlarla tüm tablo için tek bir sonuç hesapladık: toplam satış, ortalama fiyat, müşteri sayısı. Ama çoğu zaman grup bazında sonuç isteriz: "Her şehirdeki müşteri sayısı", "Her kategorideki ortalama fiyat", "Her aydaki satış toplamı"...

İşte GROUP BY tam olarak bunu yapar: satırları belirli bir sütuna göre gruplar ve her grup için aggregate hesaplama yapar.

🎯 Analoji: Bir sınıftaki öğrencileri düşün. Tüm sınıfın ortalaması yerine "erkekler ortalaması" ve "kızlar ortalaması" istiyorsun. GROUP BY, öğrencileri cinsiyete göre gruplar ve her grup için ayrı ortalama hesaplar.


Temel Sözdizimi

SELECT gruplama_sütunu, AGGREGATE_FONKSİYON(sütun)
FROM tablo
WHERE koşul           -- opsiyonel, gruplama ÖNCESİ filtre
GROUP BY gruplama_sütunu;

İlk GROUP BY Sorguları

-- Her şehirdeki müşteri sayısı
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city;
+----------+----------------+
| city     | customer_count |
+----------+----------------+
| Ankara   | 2              |
| Antalya  | 1              |
| Bursa    | 1              |
| İstanbul | 4              |
| İzmir    | 2              |
+----------+----------------+

MySQL şunu yapar:

  1. Tüm satırları city değerine göre gruplar

  2. Her grup için COUNT(*) hesaplar

  3. Her grup tek bir satır olarak döner

-- Her kategorideki ürün sayısı ve ortalama fiyat
SELECT category_id, 
       COUNT(*) AS product_count,
       ROUND(AVG(price), 2) AS avg_price,
       ROUND(MIN(price), 2) AS min_price,
       ROUND(MAX(price), 2) AS max_price
FROM products
GROUP BY category_id;

-- Her sipariş durumundaki sipariş sayısı ve toplam tutar
SELECT status,
       COUNT(*) AS order_count,
       ROUND(SUM(total_amount), 2) AS total_amount
FROM orders
GROUP BY status;

GROUP BY Kuralı: SELECT'teki Her Sütun

Altın kural: SELECT'te yazılan her sütun ya GROUP BY'da olmalı ya da aggregate fonksiyonun içinde olmalı.

-- ✅ DOĞRU — city GROUP BY'da, COUNT aggregate'te
SELECT city, COUNT(*) FROM customers GROUP BY city;

-- ❌ YANLIŞ — first_name ne GROUP BY'da ne aggregate'te
SELECT city, first_name, COUNT(*) FROM customers GROUP BY city;
-- MySQL strict mode'da: ERROR
-- Strict mode kapalıysa: rastgele bir first_name döner (tehlikeli!)

⚠️ Dikkat: MySQL'in eski sürümlerinde ve strict mode kapalıyken, GROUP BY'da olmayan sütun SELECT'te yazılabilir — ama dönen değer belirsizdir (hangi satırdan geleceği garanti değil). ONLY_FULL_GROUP_BY sql_mode'u bu hatayı engellemek için açık olmalıdır (MySQL 5.7.5+ varsayılan).

-- Kontrol et
SELECT @@sql_mode;
-- ONLY_FULL_GROUP_BY varsa → güvende

GROUP BY ile WHERE

WHERE, gruplama öncesinde satırları filtreler:

-- Aktif müşterilerin şehir dağılımı
SELECT city, COUNT(*) AS active_count
FROM customers
WHERE is_active = TRUE          -- Önce filtrele
GROUP BY city;                  -- Sonra grupla

-- 1000 TL üstü ürünlerin kategori dağılımı
SELECT category_id,
       COUNT(*) AS expensive_count,
       ROUND(AVG(price), 2) AS avg_price
FROM products
WHERE price > 1000              -- Önce 1000+ ürünleri filtrele
GROUP BY category_id;           -- Sonra kategoriye göre grupla

-- Bu ayki siparişlerin durum dağılımı
SELECT status, COUNT(*), SUM(total_amount)
FROM orders
WHERE order_date >= '2024-02-01'
GROUP BY status;

İşlem sırası:

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
              ↑           ↑
              Satır        Grup
              filtresi     filtresi

GROUP BY ile ORDER BY

-- Şehirleri müşteri sayısına göre sırala
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city
ORDER BY customer_count DESC;

-- Kategorileri ortalama fiyata göre sırala
SELECT category_id, 
       ROUND(AVG(price), 2) AS avg_price
FROM products
GROUP BY category_id
ORDER BY avg_price DESC;

-- En çok sipariş veren müşteriler
SELECT customer_id, 
       COUNT(*) AS order_count,
       SUM(total_amount) AS total_spent
FROM orders
GROUP BY customer_id
ORDER BY total_spent DESC;

GROUP BY ile LIMIT

-- En çok müşterisi olan ilk 3 şehir
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city
ORDER BY customer_count DESC
LIMIT 3;

-- En çok satan ilk 5 ürün
SELECT product_id, SUM(quantity) AS total_sold
FROM order_items
GROUP BY product_id
ORDER BY total_sold DESC
LIMIT 5;

GROUP BY ve NULL

NULL değerler tek bir grup oluşturur:

-- Telefonu NULL olan müşteriler tek grup olarak sayılır
SELECT phone IS NULL AS has_no_phone, COUNT(*)
FROM customers
GROUP BY phone IS NULL;
-- 0 (telefonu var): 9
-- 1 (telefonu yok): 1

Gerçek Dünya Örneği — Aylık Satış Raporu

-- Aylık sipariş ve gelir raporu
SELECT 
    DATE_FORMAT(order_date, '%Y-%m') AS month,
    COUNT(*) AS order_count,
    COUNT(DISTINCT customer_id) AS unique_customers,
    ROUND(SUM(total_amount), 2) AS total_revenue,
    ROUND(AVG(total_amount), 2) AS avg_order_value,
    ROUND(MAX(total_amount), 2) AS largest_order,
    COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled_count
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
ORDER BY month DESC;

Sıkça Yapılan Hatalar

  1. SELECT'te GROUP BY'da olmayan sütun kullanmak: SELECT city, first_name, COUNT(*) FROM customers GROUP BY cityfirst_name hangi satırdan gelecek? Belirsiz.

  2. WHERE ile HAVING'i karıştırmak: WHERE gruplama öncesi, HAVING gruplama sonrası filtreler. WHERE COUNT(*) > 3 yazamazsın.

  3. GROUP BY'sız aggregate ile normal sütun karıştırmak: SELECT product_name, COUNT(*) FROM products — bu ne anlama geliyor? GROUP BY ekle veya subquery kullan.

  4. GROUP BY sütununu SELECT'te yazmamak: SELECT COUNT(*) FROM orders GROUP BY status çalışır ama hangi sayının hangi status'a ait olduğunu göremezsin.


Özet

  • GROUP BY satırları belirli bir sütuna göre gruplar, her grup için aggregate hesaplar

  • SELECT'teki her sütun ya GROUP BY'da ya da aggregate fonksiyonun içinde olmalı

  • WHERE gruplama öncesinde filtreler

  • ORDER BY ve LIMIT, GROUP BY sonuçlarına uygulanabilir

  • NULL değerler tek bir grup oluşturur

  • ONLY_FULL_GROUP_BY sql_mode'u belirsiz sorguları engellemek için önemlidir