← Kursa Dön
📄 Text · 30 min

HAVING vs WHERE Farkı

Giriş — İki Farklı Filtreleme Noktası

WHERE ve HAVING ikisi de filtre yapar ama farklı aşamalarda çalışır. WHERE tek tek satırları filtreler (gruplama öncesi), HAVING ise grupları filtreler (gruplama sonrası). Bu farkı anlamak, doğru ve performanslı sorgular yazmanın anahtarıdır.

🎯 Analoji: Bir okulda sınıf ortalamalarını hesaplıyorsun. WHERE: "Sadece devamsızlığı 5'ten az olan öğrencileri dahil et" (öğrenci bazında filtre). HAVING: "Ortalaması 70'in üstünde olan sınıfları göster" (sınıf/grup bazında filtre).


HAVING — Grup Bazında Filtreleme

-- 2'den fazla müşterisi olan şehirler
SELECT city, COUNT(*) AS customer_count
FROM customers
GROUP BY city
HAVING COUNT(*) > 2;
-- Sadece İstanbul (4 müşteri) döner

-- 5000 TL üstü ortalama siparişi olan müşteriler
SELECT customer_id, 
       COUNT(*) AS order_count,
       ROUND(AVG(total_amount), 2) AS avg_amount
FROM orders
GROUP BY customer_id
HAVING AVG(total_amount) > 5000;

-- Toplam 100.000 TL üstü satış yapılan aylar
SELECT DATE_FORMAT(order_date, '%Y-%m') AS month,
       SUM(total_amount) AS revenue
FROM orders
GROUP BY DATE_FORMAT(order_date, '%Y-%m')
HAVING SUM(total_amount) > 100000;

WHERE vs HAVING Karşılaştırması

ÖzellikWHEREHAVING
Ne zaman çalışırGROUP BY'dan önceGROUP BY'dan sonra
Neyi filtrelerTek tek satırlarıGrupları
Aggregate kullanımı❌ Kullanılamaz✅ Kullanılabilir
PerformansDaha hızlı (erken filtreleme)Daha yavaş (geç filtreleme)
-- WHERE: Gruplama öncesi filtre (satır bazında)
SELECT category_id, AVG(price) AS avg_price
FROM products
WHERE is_active = TRUE              -- Önce: sadece aktif ürünleri al
GROUP BY category_id;

-- HAVING: Gruplama sonrası filtre (grup bazında)
SELECT category_id, AVG(price) AS avg_price
FROM products
GROUP BY category_id
HAVING AVG(price) > 1000;           -- Sonra: ortalaması 1000+ olan gruplar

-- İkisini birlikte kullan
SELECT category_id, 
       COUNT(*) AS product_count,
       ROUND(AVG(price), 2) AS avg_price
FROM products
WHERE is_active = TRUE              -- 1. Aktif ürünleri al
GROUP BY category_id                -- 2. Kategoriye göre grupla
HAVING COUNT(*) >= 2                -- 3. 2+ ürünü olan kategoriler
ORDER BY avg_price DESC;            -- 4. Ortalama fiyata göre sırala

İşlem sırası:

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT
         ↑                    ↑
     Satır filtresi      Grup filtresi
     (aggregate YOK)     (aggregate VAR)

Performans: WHERE vs HAVING

-- ❌ Yavaş — tüm satırları gruplar, sonra filtreler
SELECT category_id, COUNT(*) AS cnt
FROM products
GROUP BY category_id
HAVING category_id = 2;

-- ✅ Hızlı — önce filtreler (daha az satır gruplanır)
SELECT category_id, COUNT(*) AS cnt
FROM products
WHERE category_id = 2
GROUP BY category_id;

💡 İpucu: Aggregate kullanmayan filtreler WHERE'e, aggregate kullanan filtreler HAVING'e yazılmalıdır. WHERE'e yazılabilecek koşulu HAVING'e yazmak gereksiz performans kaybıdır.


HAVING'de Alias Kullanımı

-- MySQL'de HAVING'de alias kullanılabilir
SELECT city, COUNT(*) AS cnt
FROM customers
GROUP BY city
HAVING cnt > 2;  -- ✅ MySQL'de çalışır

-- PostgreSQL'de çalışmaz — aggregate'i tekrar yaz
-- HAVING COUNT(*) > 2;

Gerçek Dünya Örneği

-- VIP Müşteri Tespiti: 2+ sipariş ve toplam 10.000+ TL harcama
SELECT 
    c.customer_id,
    CONCAT(c.first_name, ' ', c.last_name) AS customer_name,
    COUNT(o.order_id) AS order_count,
    ROUND(SUM(o.total_amount), 2) AS total_spent,
    ROUND(AVG(o.total_amount), 2) AS avg_order
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.status != 'cancelled'     -- İptal siparişler hariç
GROUP BY c.customer_id, c.first_name, c.last_name
HAVING COUNT(o.order_id) >= 2     -- 2+ sipariş
   AND SUM(o.total_amount) > 10000  -- 10000+ TL harcama
ORDER BY total_spent DESC;

Sıkça Yapılan Hatalar

  1. WHERE'de aggregate kullanmak: WHERE COUNT(*) > 5 hata verir. HAVING COUNT(*) > 5 kullan.

  2. HAVING'e aggregate olmayan koşul yazmak: HAVING city = 'İstanbul' çalışır ama WHERE'de olmalıdır — performans farkı.

  3. WHERE ve HAVING'i karıştırmak: İkisi farklı aşamalarda çalışır. Aggregate koşullar HAVING'e, diğerleri WHERE'e.


Özet

  • WHERE gruplama öncesi satırları filtreler — aggregate fonksiyon kullanılamaz

  • HAVING gruplama sonrası grupları filtreler — aggregate fonksiyon kullanılabilir

  • Aggregate olmayan koşullar WHERE'de olmalı (performans için)

  • WHERE + GROUP BY + HAVING birlikte kullanılabilir

  • MySQL'de HAVING'de alias kullanılabilir