SQL GROUP BY ve HAVING farkını anlamanın en kısa yolu şudur: GROUP BY, aynı değere sahip satırları müşteri veya kategori gibi bir ölçüte göre gruplar ve her grup için COUNT, SUM ya da AVG gibi özet sonuçlar üretir. HAVING ise gruplama tamamlandıktan sonra oluşan grup sonuçlarını filtreler.
WHERE tek tek satırları gruplama işleminden önce değerlendirirken, HAVING gruplar oluşturulduktan sonra çalışır. Bu nedenle temel akışı “satırları seç → grupla → özetle → grupları filtrele” şeklinde düşünebilirsiniz.
GROUP BY ve HAVING Kısaca Ne İşe Yarar?
Bir sipariş tablosunda her satır genellikle tek bir siparişi temsil eder. Aynı müşterinin birden fazla siparişi varsa, bu satırlar ayrı ayrı tutulur. Ancak “Her müşterinin kaç siparişi var?” veya “Hangi müşterinin toplam sipariş tutarı daha yüksek?” gibi sorular sorduğunuzda tek tek satırları değil, müşteri gruplarını incelemek istersiniz.
İşte GROUP BY bu noktada kullanılır. Belirttiğiniz sütundaki aynı değerleri bir araya getirir. Ardından COUNT, SUM veya AVG gibi toplama fonksiyonlarıyla her grup için bir özet değer hesaplayabilirsiniz.
Örneğin aşağıdaki sorgu, siparişleri müşteriye göre gruplar ve her müşterinin sipariş sayısını hesaplar:
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id;
Bu sorgunun sonucunda her sipariş satırı ayrı ayrı gösterilmez. Bunun yerine her müşteri için tek bir sonuç satırı oluşur. Yani GROUP BY, veriyi yok etmez; sonuç kümesini belirlediğiniz gruplara göre özetler.
HAVING ise bu özetlenmiş grupları filtrelemek için kullanılır. Örneğin yalnızca toplam sipariş tutarı 500 TL’yi aşan müşterileri görmek istediğinizi düşünün:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 500;
Burada önce siparişler müşteri numarasına göre gruplanır, her müşterinin toplamı hesaplanır ve son olarak toplamı 500’den büyük olan gruplar bırakılır. HAVING, tek tek sipariş tutarlarını değil, her müşteriye ait hesaplanmış toplamı değerlendirir.
WHERE ile HAVING arasındaki temel farkı şöyle düşünebilirsiniz:
- WHERE: Gruplama yapılmadan önce tek tek satırları filtreler.
- GROUP BY: Kalan satırları ortak bir değere göre gruplar.
- COUNT, SUM, AVG: Her grup için özet değer üretir.
- HAVING: Oluşan grupları, özet sonuçlarına göre filtreler.
Örneğin “Sadece tamamlanmış siparişleri dikkate al ve en az iki tamamlanmış siparişi bulunan müşterileri göster” isteğinde iki farklı filtre seviyesi vardır:
SELECT customer_id, COUNT(*) AS completed_orders
FROM orders
WHERE status = 'completed'
GROUP BY customer_id
HAVING COUNT(*) >= 2;
Bu sorguda WHERE, tamamlanmamış siparişleri daha gruplama yapılmadan eler. Ardından GROUP BY müşterileri gruplar. COUNT her müşterinin tamamlanmış siparişlerini sayar. HAVING ise sayısı iki veya daha fazla olan müşteri gruplarını sonuçta bırakır.
Bu nedenle HAVING içinde çoğunlukla COUNT, SUM veya AVG gibi toplama fonksiyonları görürsünüz. WHERE aşamasında henüz grup toplamı oluşmadığı için “müşterinin toplam sipariş tutarı” gibi bir sonucu doğrudan filtrelemek yerine, bu filtreyi HAVING ile yapmak gerekir.
COUNT, SUM ve AVG Gruplu Sorgularda Ne Üretir?

GROUP BY ile birlikte kullanılan toplama fonksiyonları, her grup için ayrı bir özet üretir. Aşağıdaki küçük orders tablosunu kullanalım:
| customer_id | product | amount |
|---|---|---|
| 1 | Klavye | 300 |
| 1 | Fare | 200 |
| 2 | Monitör | 700 |
| 2 | USB kablo | 100 |
| 3 | Web kamera | 400 |
COUNT: Her gruptaki satır veya değer sayısı
COUNT, kaç satır veya kaç değer bulunduğunu sayar. Önce tahmin edin: Bu tabloda kaç farklı müşteri grubu oluşur ve her müşterinin kaç siparişi vardır?
- Müşteri 1 için iki sipariş
- Müşteri 2 için iki sipariş
- Müşteri 3 için bir sipariş
Şimdi sorguyu çalıştıralım:
SELECT customer_id, COUNT(*) AS order_count
FROM orders
GROUP BY customer_id
ORDER BY customer_id;
Beklenen çıktı şöyledir:
| customer_id | order_count |
|---|---|
| 1 | 2 |
| 2 | 2 |
| 3 | 1 |
Burada beş sipariş satırı olmasına rağmen sonuçta üç satır vardır. Çünkü sonuç satırı sayısı, sipariş sayısını değil, farklı müşteri grubu sayısını temsil eder.
COUNT(*) her grup içindeki satırları sayar. Belirli bir sütundaki NULL olmayan değerleri saymak isterseniz COUNT(column_name) biçimini kullanabilirsiniz. NULL değerlerin COUNT, SUM ve AVG üzerindeki etkisini ilerleyen bölümde ayrıca ele almak gerekir.
SUM: Her grubun sayısal toplamı
SUM, seçilen sayısal sütundaki değerleri toplar. Önce tahmin edin: Her müşterinin sipariş tutarı toplamı ne olmalıdır?
- Müşteri 1: 300 + 200 = 500
- Müşteri 2: 700 + 100 = 800
- Müşteri 3: 400
Tahmininizi yaptıktan sonra sorguya bakın:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
ORDER BY customer_id;
Beklenen çıktı:
| customer_id | total_amount |
|---|---|
| 1 | 500 |
| 2 | 800 |
| 3 | 400 |
Bu örnekte yalnızca toplam tutarı belirli bir eşiği aşan müşterileri göstermek için HAVING ekleyebilirsiniz. Örneğin toplam sipariş tutarı 500’den büyük olan müşterileri seçen sorgu şöyledir:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING SUM(amount) > 500
ORDER BY total_amount DESC;
Beklenen sonuçta yalnızca müşteri 2 yer alır; çünkü müşteri 2’nin toplamı 800’dür. Müşteri 1’in toplamı tam olarak 500 olduğu için “500’den büyük” koşulunu karşılamaz. Eşiğe eşit değerleri de dahil etmek isterseniz >= 500 kullanmanız gerekir.
AVG: Her grubun sayısal ortalaması
AVG, bir grubun sayısal değerlerinin ortalamasını hesaplar. Önce tahmin edin: Her müşterinin sipariş başına ortalama tutarı ne olur?
- Müşteri 1: (300 + 200) / 2 = 250
- Müşteri 2: (700 + 100) / 2 = 400
- Müşteri 3: 400 / 1 = 400
Tahminden sonra sorguyu çalıştırabilirsiniz:
SELECT customer_id, AVG(amount) AS average_amount
FROM orders
GROUP BY customer_id
ORDER BY customer_id;
Beklenen çıktı:
| customer_id | average_amount |
|---|---|
| 1 | 250 |
| 2 | 400 |
| 3 | 400 |
AVG, toplam tutarı sipariş sayısına bölerek her grup için ortalama değer üretir. Bu yüzden AVG sonucunu yorumlarken grubun kaç satırdan oluştuğunu da bilmek önemlidir. Tek siparişi olan müşteriyle çok sayıda siparişi olan müşterinin ortalaması aynı anlamı taşımayabilir.
GROUP BY olmadan ve GROUP BY ile özet arasındaki fark
Toplama fonksiyonlarını GROUP BY olmadan kullandığınızda tüm tablo tek bir grup gibi değerlendirilir. Önce tahmin edin: Beş siparişin toplam tutarı, sipariş sayısı ve ortalama tutarı nedir?
- Toplam sipariş sayısı: 5
- Toplam tutar: 1.700
- Ortalama sipariş tutarı: 340
SELECT
COUNT(*) AS order_count,
SUM(amount) AS total_amount,
AVG(amount) AS average_amount
FROM orders;
Beklenen çıktı tek satırdır:
| order_count | total_amount | average_amount |
|---|---|---|
| 5 | 1700 | 340 |
Aynı fonksiyonların sonuna GROUP BY customer_id eklendiğinde ise tek bir genel sonuç yerine müşteri başına ayrı sonuçlar oluşur. Yani farkı belirleyen yalnızca COUNT, SUM veya AVG değildir; bu fonksiyonların hangi kapsamda çalışacağını GROUP BY belirler.
Özetle, GROUP BY olmadan “tüm siparişlerin özeti”, GROUP BY ile “her müşteri veya kategori için ayrı özet” elde edilir. Sorgularınızı yazmadan önce beklenen grup sayısını, toplamları ve ortalamaları kâğıt üzerinde tahmin etmek; yazım hatalarını ve yanlış yorumları fark etmenin pratik bir yoludur.
Üç Müşterinin Siparişleriyle GROUP BY Sonucunu Tahmin Et
GROUP BY mantığını anlamanın en kolay yolu, tek tek sipariş satırlarına bakmak yerine “müşteri başına özet” üretmektir. Aşağıdaki örnekte üç müşterinin birden fazla siparişi bulunuyor. Sorguyu çalıştırmadan önce her müşteri için kaç sipariş olduğunu, toplam tutarın ne kadar olduğunu ve ortalama sipariş tutarının yaklaşık kaç çıkacağını tahmin edin.
| Müşteri | Sipariş Tutarı | Sipariş Durumu |
|---|---|---|
| Ayşe | 1200 | Tamamlandı |
| Ayşe | 800 | Bekliyor |
| Ayşe | 500 | Tamamlandı |
| Mert | 1500 | Tamamlandı |
| Mert | 700 | İptal |
| Mert | 900 | Tamamlandı |
| Zeynep | 400 | Bekliyor |
| Zeynep | 600 | Tamamlandı |
| Zeynep | 1000 | Tamamlandı |
Önce tahmin edin: Her müşteri için sonuç ne olmalı?
İlk sorguda sipariş durumlarını dikkate almayacağız. Bu nedenle her müşterinin tablodaki bütün siparişleri hesaba katılacak.
- Ayşe’nin 3 siparişi vardır. Toplam tutarı 1200 + 800 + 500, yani 2500’dür.
- Mert’in 3 siparişi vardır. Toplam tutarı 1500 + 700 + 900, yani 3100’dür.
- Zeynep’in 3 siparişi vardır. Toplam tutarı 400 + 600 + 1000, yani 2000’dir.
Ortalama sipariş tutarını bulmak için toplam tutarı sipariş sayısına bölebilirsiniz. Örneğin Ayşe için 2500 / 3 işlemi yaklaşık 833,33 sonucunu verir.
GROUP BY ile müşteri başına özet oluşturma
Aşağıdaki sorguda musteri_adi alanı gruplama ölçütüdür. COUNT(*) her müşterinin kaç satırla temsil edildiğini, SUM(tutar) sipariş tutarlarının toplamını, AVG(tutar) ise ortalama sipariş tutarını hesaplar.
SELECT
musteri_adi,
COUNT(*) AS siparis_sayisi,
SUM(tutar) AS toplam_tutar,
AVG(tutar) AS ortalama_tutar
FROM siparisler
GROUP BY musteri_adi
ORDER BY musteri_adi;
Beklenen sonuç aşağıdaki gibi olur:
| Müşteri | Sipariş Sayısı | Toplam Tutar | Ortalama Tutar |
|---|---|---|---|
| Ayşe | 3 | 2500 | 833,33 |
| Mert | 3 | 3100 | 1033,33 |
| Zeynep | 3 | 2000 | 666,67 |
Buradaki önemli nokta, sorgunun artık her siparişi ayrı ayrı göstermemesidir. Normal bir SELECT sorgusunda dokuz sipariş satırı görebilirdiniz. GROUP BY musteri_adi kullanıldığında ise sonuç üç satıra iner; çünkü her müşteri tek bir grup hâline gelir.
Bu değişikliğe sonuç granülerliğinin değişmesi denir. Başlangıçtaki veri “sipariş başına” ayrıntı düzeyindeyken, GROUP BY sonrasındaki veri “müşteri başına” özet düzeyindedir. Bu nedenle sorguya eklediğiniz her sütunun bu yeni düzeyle uyumlu olması gerekir. Müşterinin adı grubu tanımlar; toplam ve ortalama gibi fonksiyonlar ise o grubun içindeki siparişlerden hesaplanır.
AVG eklenince hangi soruya cevap verilir?
SUM(tutar) müşterinin bütün siparişlerinin toplam değerini verir. AVG(tutar) ise müşterinin tek bir siparişinin ortalama değerini gösterir. Bu iki sonuç birbirinin alternatifi değildir; farklı sorulara cevap verir:
COUNT(*): Müşteri kaç sipariş vermiş?SUM(tutar): Bu siparişlerin toplam parasal değeri nedir?AVG(tutar): Bir siparişin ortalama tutarı nedir?
Örneğin Mert’in toplam tutarı en yüksek müşteridir. Ancak ortalama tutarını yorumlarken iptal edilmiş 700 tutarındaki siparişin de hesaba katıldığını unutmamak gerekir. İş kuralına göre yalnızca tamamlanmış siparişler incelenecekse önce satırlar filtrelenmeli, ardından gruplama yapılmalıdır.
WHERE Satırları, HAVING Grupları Neden Filtreler?

WHERE satırları, HAVING ise grupları filtreler. Bu farkı aynı sipariş tablosu üzerinde üç aşamada düşünebilirsiniz: önce uygun sipariş satırları seçilir, sonra seçilen satırlar müşteri bazında gruplanır, en son grup toplamları belirli bir koşula göre elenir.
1. Aşama: WHERE ile sipariş satırlarını daraltma
Şimdi yalnızca durumu “Tamamlandı” olan siparişleri inceleyelim. Bu aşamada henüz müşteri toplamı hesaplanmış değildir. WHERE, tablodaki her sipariş satırını tek tek kontrol eder ve durumu uygun olmayan satırları gruplama başlamadan önce dışarıda bırakır.
Örneğin Ayşe’nin 800 tutarındaki “Bekliyor” siparişi, Mert’in 700 tutarındaki “İptal” siparişi ve Zeynep’in 400 tutarındaki “Bekliyor” siparişi hesaplamaya alınmaz.
2. Aşama: Kalan satırları GROUP BY ile özetleme
WHERE sonrasında elde kalan siparişler şunlardır:
- Ayşe: 1200 ve 500
- Mert: 1500 ve 900
- Zeynep: 600 ve 1000
Bu satırlar daha sonra müşteri adına göre gruplanır. Böylece her müşteri için tamamlanmış siparişlerin toplamı hesaplanabilir.
3. Aşama: HAVING ile grup toplamını filtreleme
Amacımız, tamamlanmış siparişlerinin toplamı 2000’den büyük olan müşterileri bulmak olsun. Bu koşul tek bir sipariş satırını değil, müşterinin grubundan üretilen toplam değeri ilgilendirir. Bu nedenle koşul WHERE yerine HAVING bölümünde yazılır.
SELECT
musteri_adi,
COUNT(*) AS tamamlanan_siparis_sayisi,
SUM(tutar) AS tamamlanan_toplam
FROM siparisler
WHERE durum = 'Tamamlandı'
GROUP BY musteri_adi
HAVING SUM(tutar) > 2000
ORDER BY tamamlanan_toplam DESC;
Sorguyu çalıştırmadan önce sonucu tahmin edin. Yalnızca tamamlanmış siparişleri topladığımız için:
- Ayşe’nin tamamlanmış sipariş toplamı 1200 + 500 = 1700 olur.
- Mert’in tamamlanmış sipariş toplamı 1500 + 900 = 2400 olur.
- Zeynep’in tamamlanmış sipariş toplamı 600 + 1000 = 1600 olur.
2000 eşiğini yalnızca Mert geçtiği için beklenen çıktı şöyledir:
| Müşteri | Tamamlanan Sipariş Sayısı | Tamamlanan Toplam |
|---|---|---|
| Mert | 2 | 2400 |
Bu örnekte sorgunun mantığı sırasıyla şöyledir: WHERE durum = 'Tamamlandı' satırları azaltır, GROUP BY musteri_adi kalan satırları müşteri bazında birleştirir, HAVING SUM(tutar) > 2000 ise oluşan müşteri gruplarını toplamlarına göre filtreler.
WHERE ve HAVING farkını kısa tabloda görün
| Özellik | WHERE | HAVING |
|---|---|---|
| Filtrelediği şey | Tek tek satırlar | Gruplar |
| Ne zaman düşünülür? | Gruplama öncesinde | Gruplama sonrasında |
| Tipik koşul | durum = 'Tamamlandı' |
SUM(tutar) > 2000 |
| Aggregate sonuç kullanımı | Genellikle kullanılmaz; grup sonucu henüz oluşmamıştır | COUNT, SUM ve AVG sonuçlarıyla kullanılabilir |
“Toplamı 2000’den büyük müşteriler” ifadesindeki toplam, tek bir satırda hazır duran bir değer değildir. SQL’in ilgili müşterinin birden fazla siparişini bir araya getirerek hesapladığı grup sonucudur. Bu yüzden SUM(tutar) > 2000 koşulunu WHERE bölümüne koymak mantıksal olarak doğru değildir; WHERE henüz oluşmamış müşteri toplamını değerlendiremez.
Aynı mantık sipariş sayısı için de geçerlidir. “En az üç tamamlanmış siparişi olan müşteriler” demek istiyorsanız HAVING COUNT(*) >= 3 kullanılır. Çünkü burada kontrol edilen değer, tek bir sipariş satırı değil, müşteri grubundaki satırların sayısıdır.
Bu ayrımı pekiştirmek için temel SQL kavramlarınızı SQL bilgi testi ile sınayabilir; özellikle WHERE, GROUP BY ve HAVING bölümlerinin hangi sırayla ve hangi amaçla kullanıldığını uygulamalı olarak kontrol edebilirsiniz.
NULL Değerleri ve Alias Kullanımında Dikkat Edilecekler
SQL'de NULL, sıfır veya boş metin anlamına gelmez. NULL, ilgili değer hakkında bilinmeyen, mevcut olmayan ya da belirtilmemiş bir durumu ifade eder. Bu nedenle NULL ile matematiksel işlem yaptığınızda veya sayım gerçekleştirdiğinizde, normal bir sayı ya da boş karakter gibi davranmasını beklememelisiniz.
COUNT(*) ile COUNT(sütun) arasındaki fark
COUNT(*) sorgu sonucuna giren satırların tamamını sayar. Satırdaki belirli sütunların NULL olup olmaması bu sayımı etkilemez. Buna karşılık COUNT(sütun_adı), yalnızca belirtilen sütunda NULL olmayan değerleri sayar.
Örneğin üç sipariş satırının bulunduğu ve bunlardan birinde tutar değerinin NULL olduğu bir grubu düşünelim. Önce tahmin edin:
COUNT(*)sonucu kaç olur?COUNT(tutar)sonucu kaç olur?
Beklenen sonuç, COUNT(*) için 3, COUNT(tutar) için ise 2'dir. Çünkü ilk ifade satır sayısını, ikinci ifade ise yalnızca tutarı bilinen satırları sayar.
SELECT
COUNT(*) AS siparis_satiri,
COUNT(tutar) AS tutari_bilinen_siparis
FROM siparisler;
Bu ayrım, özellikle müşteri başına sipariş sayısı ile tutarı kayıtlı sipariş sayısını birbirinden ayırmak istediğinizde önemlidir. Aynı müşteri için COUNT(*) üç sipariş gösterebilirken, COUNT(tutar) tutarı eksik olan bir siparişi hesaba katmayabilir.
SUM ve AVG NULL değerlerle nasıl davranır?
SUM() ve AVG(), yaygın SQL kullanımlarında hesaplama sırasında NULL değerleri çoğunlukla dikkate almaz. Örneğin bir grubun tutarları 100, 200 ve NULL ise ortalama çoğu durumda 100 ve 200 üzerinden hesaplanır; NULL sıfır gibi değerlendirilmez.
Ancak bir gruptaki tüm değerler NULL ise veya aggregate fonksiyonun içinde farklı bir ifade kullanılıyorsa sonuç NULL olabilir. Bu davranışı, kullandığınız veritabanı lehçesi ve sorgunun tam ifadesiyle birlikte kontrol etmek gerekir. PostgreSQL aggregate fonksiyon belgeleri bu tür fonksiyonların boş veya NULL değer içeren gruplardaki sonuçlarını ayrı ayrı açıklar.
Bu yüzden “tutarı olmayan müşterinin toplamı sıfırdır” cümlesi teknik bir zorunluluk değil, bir iş kuralı kararıdır. Eğer uygulamanızda bu kural geçerliyse, sonucu açıkça dönüştürebilirsiniz:
SELECT
musteri_id,
COALESCE(SUM(tutar), 0) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id;
COALESCE, ilk NULL olmayan değeri döndürür. Burada SUM(tutar) sonucu NULL ise 0 gösterilir. Fakat her durumda NULL'ı sıfıra çevirmek doğru değildir. Örneğin “henüz hesaplanmadı” ile “gerçekten sıfır” aynı anlama geliyorsa bu dönüşüm kullanılabilir; iki durumun ayrılması gerekiyorsa NULL değerini korumak daha doğru olabilir.
Alias kullanımı sorguyu okunabilir hâle getirir
Alias, bir sütuna veya hesaplanmış ifadeye sorgu çıktısında daha anlaşılır bir ad vermek için kullanılır. Özellikle COUNT(*), SUM(tutar) ve AVG(tutar) gibi uzun ifadelerde sonuç sütunlarını anlamlı isimlerle göstermek okunabilirliği artırır.
SELECT
musteri_id,
COUNT(*) AS siparis_sayisi,
SUM(tutar) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id;
Buradaki siparis_sayisi ve toplam_tutar ifadeleri alias'tır. Ancak SELECT içinde verilen alias'ın aynı sorgunun WHERE, HAVING veya GROUP BY bölümünde kullanılabilmesi bütün SQL lehçelerinde aynı değildir. Bazı veritabanları belirli bölümlerde alias kullanımına izin verirken bazıları ifadeyi yeniden yazmanızı isteyebilir.
Özellikle WHERE içinde aggregate sonucu kullanmaya çalışmak genellikle doğru yaklaşım değildir; çünkü WHERE satırları gruplamadan önce filtreler. Grup sonucuna göre filtreleme yapılacaksa aggregate ifadeyi HAVING içinde tekrar yazmak daha taşınabilir bir yöntemdir:
SELECT
musteri_id,
SUM(tutar) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id
HAVING SUM(tutar) > 1000;
Alias kullanımı lehçeye göre destekleniyor olsa bile, ekip içinde farklı veritabanları kullanılabilecekse iki güvenli seçenek vardır:
- Aggregate ifadeyi
HAVINGiçinde açıkça tekrar etmek. - Önce alt sorguda alias'ı üretip dış sorguda bu alias ile filtreleme yapmak.
Böylece sorgunun yalnızca tek bir veritabanının özel davranışına bağlı kalma ihtimali azalır.
Doğru GROUP BY ve HAVING Sorgusu Yazma Kontrol Listesi
Gruplu bir SQL sorgusunu çalıştırmadan önce yalnızca sözdizimini değil, sorgunun hangi aşamada ne yaptığını da kontrol edin. Aşağıdaki liste, GROUP BY ve HAVING kullanımındaki yaygın karışıklıkları kısa sürede fark etmenize yardımcı olur.
- Satır mı, grup mu filtreleniyor? Tek tek siparişlerin özelliğine göre filtreleme yapıyorsanız
WHERE; müşterilerin toplamı veya sipariş sayısı gibi grup sonucuna göre filtreleme yapıyorsanızHAVINGdüşünülmelidir. - Hangi aggregate fonksiyonu gerekiyor? Satır sayısı için
COUNT, toplam içinSUM, ortalama içinAVGkullanılır. - Seçilen aggregate olmayan sütunlar GROUP BY içinde mi?
SELECTbölümünde doğrudan seçtiğiniz ve aggregate fonksiyonla sarmalamadığınız sütunların genellikleGROUP BYiçinde bulunması gerekir. - WHERE koşulu gruplamadan önce uygulanabilir mi? Tarih, durum, müşteri türü veya tek bir sütundaki değer gibi satır bazlı bir koşul varsa bu koşulu önce
WHEREile uygulamak daha anlaşılırdır. - Grup sonucuna ilişkin koşul HAVING'e mi yazıldı?
SUM(tutar) > 1000veyaCOUNT(*) > 2gibi koşullar grup oluştuktan sonra değerlendirilmelidir. - NULL değerlerin sayımı ve ortalamayı değiştirme ihtimali kontrol edildi mi?
COUNT(*)ileCOUNT(sütun)aynı sonucu vermeyebilir.AVGveSUMiçin de eksik değerlerin iş kuralındaki anlamı belirlenmelidir. - Alias kullanımı seçilen SQL lehçesinde doğrulandı mı? Alias'ı
HAVING,WHEREveyaGROUP BYiçinde kullanmadan önce kullandığınız veritabanının kurallarını kontrol edin. - Beklenen sonuç sorgu çalıştırılmadan önce tahmin edildi mi? Önce her grubun kaç satır içereceğini ve toplamının ne olması gerektiğini düşünün; sonra sorgunun çıktısını bu tahminle karşılaştırın.
SQL ifadelerinin değerlendirme aşamasını karşılaştırma
| Sütun/ifade | Hangi aşamada değerlendirilir? | Tipik kullanım |
|---|---|---|
| Normal sütun koşulu | Gruplamadan önce | WHERE durum = 'tamamlandi' |
| GROUP BY sütunu | Benzer satırları gruplarken | GROUP BY musteri_id |
| Aggregate ifade | Her grup için özet üretilirken | SUM(tutar), COUNT(*) |
| HAVING koşulu | Gruplar oluştuktan sonra | HAVING COUNT(*) > 2 |
| Alias | Sonuç sütununu adlandırırken | SUM(tutar) AS toplam_tutar |
Yanlış ve düzeltilmiş yaklaşım
Bir müşterinin toplam sipariş tutarı 1000'i geçiyorsa onu göstermek istediğinizi varsayalım. Önce tahmin edin: Bu koşul tek bir sipariş satırını mı, yoksa müşterinin bütün siparişlerinin toplamını mı ilgilendiriyor? Cevap grup toplamı olduğu için koşul HAVING içinde yer almalıdır.
Yanlış yaklaşım, aggregate sonucu satır bazında filtreleyen bir WHERE koşulu yazmaktır:
SELECT
musteri_id,
SUM(tutar) AS toplam_tutar
FROM siparisler
WHERE SUM(tutar) > 1000
GROUP BY musteri_id;
Bu sorguda SUM(tutar) henüz grup oluşturulmadan önce kullanılmaya çalışıldığı için birçok SQL lehçesinde hata alınır. Düzeltilmiş yaklaşım şöyledir:
SELECT
musteri_id,
SUM(tutar) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id
HAVING SUM(tutar) > 1000;
Buradaki temel karar kuralı basittir: satır koşulu WHERE'e, grup koşulu HAVING'e yazılır. Sorguyu çalıştırmadan önce sonucu tahmin etmek de bu ayrımı pekiştirir. Örneğin müşterilerin toplamlarını kâğıt üzerinde hesaplayıp yalnızca eşiği geçen grupları listeleyebilir, ardından SQL çıktısında aynı müşterilerin görünüp görünmediğini kontrol edebilirsiniz.
Bu konudaki temel sorgu bilginizi kendi başınıza ölçmek isterseniz, ücretsiz yazılım bilgi testi üzerinden özellikle filtreleme, aggregate fonksiyonlar ve sorgu mantığıyla ilgili seviyenizi gözden geçirebilirsiniz.
Sık Sorulan Sorular
GROUP BY kullanmadan COUNT, SUM veya AVG yazılabilir mi?
Evet. GROUP BY kullanılmadığında aggregate fonksiyon çoğu yaygın SQL kullanımında sorgunun uygun satırlarının tamamını tek bir grup gibi değerlendirir. Örneğin SELECT COUNT(*) FROM siparisler toplam satır sayısını, SELECT SUM(tutar) FROM siparisler ise uygun tutarların toplamını döndürebilir. Her müşteri için ayrı sonuç istiyorsanız musteri_id gibi bir sütunla GROUP BY kullanmanız gerekir.
WHERE yerine HAVING kullanırsam sorgu neden hata verebilir veya farklı sonuç döndürebilir?
WHERE satırları gruplamadan önce, HAVING ise gruplar oluşturulduktan sonra filtreler. Bu nedenle normal bir sütun koşulunu HAVING'e taşımak bazı lehçelerde hata verebilir veya sorgunun anlamını değiştirebilir. Özellikle COUNT ya da SUM gibi aggregate sonuçlarına dayanan koşullar HAVING'de; tek bir satırdaki değere dayanan koşullar ise genellikle WHERE'de yazılmalıdır.
HAVING içinde SUM veya COUNT sonucuna göre nasıl filtreleme yapılır?
Önce gruplama sütununu yazın, ardından aggregate ifadeyi HAVING koşulunda kullanın. Örneğin HAVING SUM(tutar) > 1000 toplamı 1000'i aşan grupları, HAVING COUNT(*) > 2 ise üçten fazla satıra sahip grupları seçer. Alias kullanımının lehçeye göre değişebileceğini unutmayın; taşınabilirlik için aggregate ifadeyi açıkça yazmak çoğu durumda daha güvenlidir.
COUNT(*) ile COUNT(sütun_adı) arasındaki fark nedir?
COUNT(*) sorguya giren tüm satırları sayar. COUNT(sütun_adı) ise yalnızca o sütunda NULL olmayan değerleri sayar. Bu nedenle ilgili sütunda eksik değerler varsa iki sonuç farklı olabilir. Satır sayısını öğrenmek için genellikle COUNT(*), belirli bir sütunun dolu değerlerini saymak için ise COUNT(sütun_adı) kullanılır.
GROUP BY ve HAVING ayrımını doğru kurmanın en iyi yolu, önce beklenen grupları ve sonuçları tahmin etmek, ardından sorguyu çalıştırıp çıktıyı bu tahminle karşılaştırmaktır.