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:
Tüm satırları
citydeğerine göre gruplarHer grup için
COUNT(*)hesaplarHer 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_BYsql_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üvendeGROUP 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 filtresiGROUP 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): 1Gerç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
SELECT'te GROUP BY'da olmayan sütun kullanmak:
SELECT city, first_name, COUNT(*) FROM customers GROUP BY city—first_namehangi satırdan gelecek? Belirsiz.WHERE ile HAVING'i karıştırmak: WHERE gruplama öncesi, HAVING gruplama sonrası filtreler.
WHERE COUNT(*) > 3yazamazsın.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.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 BYsatırları belirli bir sütuna göre gruplar, her grup için aggregate hesaplarSELECT'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_BYsql_mode'u belirsiz sorguları engellemek için önemlidir
AI Asistan
Sorularını yanıtlamaya hazır