GROUP BY ile Satırları Gruplara Toplama
COUNT, SUM, AVG gibi aggregate fonksiyonlar bir sürü satırı tek bir değere indirger. GROUP BY ise bu işi kategori bazında yapmanı sağlar: müşteri başına, ay başına, durum başına tek bir sayı. Her benzersiz değer (ya da değer kombinasyonu) sonuçta tek bir satır olarak karşına çıkar.
Üç müşteri, üç satır sonuç. Başlangıçtaki altı satır artık yok — her müşteri için ayrı bir kovaya toplandı ve COUNT(*) ile SUM(amount) her kovanın içinde ayrı ayrı hesaplandı.
Olayı şöyle düşün: GROUP BY customer demek, "aynı customer değerine sahip tüm satırları tek bir grup olarak değerlendir" demek. Aggregate fonksiyonlar da bundan sonra her grup üzerinde ayrı ayrı çalışır.
SELECT Listesinde Neler Yer Alabilir?
Burası genelde kafa karıştırır. GROUP BY kullandığında, SELECT listesindeki her sütun ya GROUP BY ifadesinde geçmeli ya da bir aggregate fonksiyonun içinde olmalı. Aksi hâlde değer belirsiz kalır — gruptaki hangi satırdan alınacağı belli değildir ki.
SELECT region, rep, SUM(amount) yazıp GROUP BY region koyarsanız, SQLite bunu sorunsuz çalıştırır (başka veritabanları reddederken o esnek davranır), ama rep değeri grup içinden rastgele seçilir. Her bölge için bir temsilci ismi alırsınız, ama hangisinin geleceğinin garantisi yoktur. Buna bel bağlamayın — gösterdiğiniz ve aggregate olmayan her sütunu GROUP BY'a ekleyin.
HAVING ile Gruplara Aggregation Sonrası Filtre Uygulama
WHERE satırları gruplama öncesinde filtreler. HAVING ise grupları gruplama sonrasında filtreler. SQLite'da where ile having farkı aslında bundan ibaret. İşte tam da bu yüzden COUNT(*) > 1 ifadesini WHERE içine koyamazsınız — çünkü WHERE çalıştığı anda ortada henüz bir sayım yoktur.
Cleo yalnızca tek bir sipariş verdiği için onun grubu eleniyor. Geriye Ada ve Boris kalıyor. Koşul, tek tek satırlara değil, her grubun birleştirilmiş (aggregate) değerine uygulanıyor.
SQLite'ta SELECT listesindeki sütun takma adlarını (alias) doğrudan HAVING içinde kullanabilirsiniz — buna izin verilir:
SUM(amount) ifadesini HAVING içinde tekrar tekrar yazmaktan çoğu zaman daha okunaklı olur.
WHERE ve HAVING farkı: İkisini birlikte kullanın
Bu iki cümleyi birbirinin alternatifi gibi düşünmeyin. WHERE, gruplamaya hangi satırların dahil olacağını daraltır; HAVING ise hangi grupların sonuca yansıyacağını süzer. Gerçek hayattaki sorguların büyük çoğunluğu ikisini birden kullanır.
Yukarıdan aşağıya, çalışma sırasıyla okuyalım:
WHERE status = 'paid'— iade edilmiş satırları tamamen ele.GROUP BY customer— kalan satırları müşteriye göre grupla.SUM(amount)her grup için ayrı ayrı çalışır.HAVING SUM(amount) > 75— yalnızca eşiği geçen grupları tut.
Boris (80 + 20 = 100) ve Cleo (200) elemeyi geçer. Ada'nın tek ödenmiş siparişi 50 olduğu için eşiğin altında kalır.
Birden Fazla Koşul ve Birden Fazla Gruplama Sütunu
HAVING, WHERE ile aynı mantıksal operatörleri kabul eder — AND, OR, NOT — ayrıca birden fazla sütuna göre gruplayarak alt kırılımlar elde edebilirsiniz:
Her (region, quarter) ikilisi ayrı bir grup oluşturuyor. Buradaki HAVING koşulu hem toplamın 100'ün üzerinde olmasını hem de en az iki anlaşma bulunmasını istiyor. Bu kriterleri yalnızca ('North', 'Q1') ve ('South', 'Q2') karşılıyor.
Pratik Bir Kullanım: Tekrarlanan Kayıtları Bulmak
Bir kolondaki tekrar eden değerleri yakalamanın en yaygın yolu GROUP BY ... HAVING COUNT(*) > 1 sorgusudur:
İki adet mükerrer kayıt karşımıza çıkıyor. Buradan sonrası genelde şuna karar vermekle geçer: hesapları birleştirmek mi, UNIQUE kısıtı eklemek mi, yoksa veriyi temizlemek mi gerekiyor? Ama bu tarz tespit sorgularının iskeleti her seferinde aynıdır.
GROUP BY Olmadan HAVING Kullanımı
Pek alışılmış olmasa da SQLite bunu kabul eder. GROUP BY yazmadığınızda tüm sonuç kümesi tek bir gruba indirgenir; HAVING da bu tek grubu bir bütün olarak süzer. Yani ya tüm toplulaştırılmış değerleri görürsünüz ya da hiçbir şey:
Tek bir sonuç satırı dönmesinin sebebi, toplamın 160 olması. Eşiği > 200 yapın; sorgu hiç satır döndürmez. Pratikte HAVING'i neredeyse her zaman GROUP BY ile birlikte kullanırsınız — ama dilin bunu zorunlu kılmadığını bilmek de iyidir.
Kısa Özet
GROUP BY, satırları anahtara göre kovalara toplar; aggregate fonksiyonlar her kovanın içinde çalışır.SELECTiçindeki aggregate olmayan her sütunGROUP BYiçinde de yer almalı.WHEREgruplamadan önce satırları filtreler;HAVINGise gruplamadan sonra grupları filtreler.COUNT(*)veSUM(...)gibi aggregate ifadelerHAVINGiçine yazılır, aslaWHERE'e değil.HAVINGbirden fazla koşulu birleştirebilir veSELECT'teki takma adlara (alias) referans verebilir.
Sırada: Foreign Key'ler
Tek bir tabloyu gruplayıp özetlemek işe yarar, ama gerçek hayattaki şemaların çoğu veriyi birden fazla tabloya yayar — siparişler bir tabloda, müşteriler başka bir tabloda, ürünler bambaşka bir yerde. Foreign key'ler de bu tabloları birbirine bağlayıp ilişkilerin tutarlı kalmasını sağlayan mekanizmadır. Bir sonraki bölümün konusu tam olarak bu.
Sıkça Sorulan Sorular
SQLite'ta WHERE ile HAVING arasındaki fark nedir?
WHERE, satırları gruplama yapılmadan önce tek tek filtreler. HAVING ise toplama işleminden sonra grupları filtreler. Yani WHERE amount > 100 sadece tutarı 100'den büyük olan satırları bırakır; HAVING SUM(amount) > 100 ise toplamı 100'ü geçen grupları bırakır. COUNT veya SUM gibi toplama (aggregate) fonksiyonları WHERE içinde kullanılamaz — zaten HAVING tam da bunun için var.
SQLite'ta GROUP BY olmadan HAVING kullanılabilir mi?
Evet, kullanılabilir. GROUP BY olmadığında SQLite tüm sonuç kümesini tek bir grup gibi ele alır ve HAVING o tek grubu bir bütün olarak filtreler. Sorgu ya tek satır döner ya da hiç satır dönmez. Pratikte pek karşılaşılan bir durum değildir; genelde HAVING varsa yanında bir GROUP BY da bulunur.
SQLite'ta grupları COUNT'a göre nasıl filtelerim?
Toplama fonksiyonunu WHERE içine değil, HAVING içine yazmalısın. Örneğin SELECT customer_id, COUNT(*) FROM orders GROUP BY customer_id HAVING COUNT(*) > 1 sorgusu birden fazla siparişi olan müşterileri döndürür. SQLite'ta HAVING içinde SELECT listesindeki kolon takma adını (alias) da kullanabilirsin.