PostgreSQL “must appear in the GROUP BY clause” hatası, SELECT listesindeki her sütunun ya GROUP BY içinde yer almaması ya da COUNT, SUM ve MAX gibi bir toplama fonksiyonunda kullanılmaması nedeniyle oluşur. Toplama fonksiyonu, birden fazla satırdaki değeri tek bir sonuçta birleştirir. Bu nedenle PostgreSQL, gruplandırılmış bir sorguda hangi sütunların tek bir değere dönüştürüldüğünü açıkça görmek ister.
Hatanın çözümü, sorgunun üretmesini istediğin sonuç düzeyini belirlemekle başlar. Amacın müşteri başına toplam tutarı bulmaksa yalnızca müşteriyi gruplarsın; ürün adını da göstermek istiyorsan ürünün gruplama mantığındaki yerini ayrıca açıklaman gerekir.
PostgreSQL bu GROUP BY hatasını neden verir?
PostgreSQL bu hatayı, bir grubun içinden gruplandırılmamış bir sütun için hangi değerin döndürüleceği belirli olmadığı için verir. SUM(tutar) gibi bir toplama fonksiyonu birden fazla siparişin tutarını tek sayıda birleştirebilir, ancak urun sütunu kendiliğinden tek bir ürüne dönüşmez.
SELECT
musteri_id,
urun,
SUM(tutar) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id;
Bu sorguda musteri_id gruplama sütunudur ve SUM(tutar) her müşteri grubundaki tutarları toplar. Hatanın kaynağı olan urun ise ne GROUP BY içinde yer alır ne de bir toplama fonksiyonunun parçasıdır.
Mesajdaki column ... must appear in the GROUP BY clause or be used in an aggregate function bölümünü üç parçaya ayırarak okuyabilirsin. column ifadesi PostgreSQL’in sorunlu sütunu gösterdiğini, must appear in the GROUP BY clause bölümü bu sütunun gruplama listesine eklenebileceğini, or be used in an aggregate function bölümü ise anlamlı olduğu durumlarda bir toplama fonksiyonuyla işlenebileceğini anlatır. Örneğin urun sütununu yalnızca hatayı susturmak için MAX(urun) içine almak doğru bir sonuç üretmeyebilir; seçilecek ürünün neden maksimum değer olması gerektiği açıklanmalıdır.
PostgreSQL’in SELECT ve GROUP BY belgelerinde belirtilen temel mantık, toplama yapılan sorgularda seçilen her ifadenin gruplandırılmış olması veya her grup için tek bir değere indirgenmesidir. Bu kural, sonuç satırının hangi anlamı taşıdığını sorgu içinde açık tutar.
Gruplama yapılınca hangi değer seçilecek?

Gruplama yapılınca PostgreSQL, grubun içinde birden fazla farklı değer bulunan gruplandırılmamış bir sütundan rastgele bir değer seçmez. Çünkü aşağıdaki örnekte aynı müşteriye ait iki farklı ürün vardır:
101, Klavye, 800101, Fare, 450102, Monitör, 3000
musteri_id ile gruplandığında 101 numaralı müşteri için SUM(tutar) sonucu 1250 olarak hesaplanabilir. Buna karşılık aynı grubun urun değeri hem “Klavye” hem de “Fare” olduğu için tek bir ürün adı seçmek belirsizdir. PostgreSQL’in hatayı vermesinin nedeni bu belirsizliği sorguyu yazan kişinin çözmesini istemesidir.
Sorguyu düzeltmeden önce her sonuç satırının hangi düzeyi temsil edeceğine karar ver:
- Müşteri ve ürün başına sonuç:
musteri_idile birlikteurunde gruplandırılır. Böylece “101 müşterisi, Klavye ürünü” ayrı bir grup olur. - Yalnızca müşteri başına sonuç:
urunseçilmez, müşterinin tüm sipariş tutarları tek bir toplamda birleştirilir. - Her müşteriden tek bir sipariş satırı: Burada amaç toplama yapmak değildir. En yüksek tutarlı, en yeni veya belirli bir koşula uyan satırı seçmek için
DISTINCT ONya daROW_NUMBER()gibi satır seçme yöntemleri gerekir.
Bu ayrım yapılmadan GROUP BY listesine rastgele sütun eklemek, hatayı ortadan kaldırsa bile istenen sonucu vermeyebilir. Önce sonuç satırının neyi temsil edeceğini belirlemek, ardından uygun gruplama veya satır seçme yöntemini kullanmak gerekir.
Sipariş sorgusunu üç farklı şekilde nasıl düzeltirsin?
Sorguyu düzeltirken önce sonuçta hangi satır anlamını istediğini belirlemelisin. Müşteri ve ürün bazında özet istiyorsan gruplamaya urun sütununu eklersin; müşteri başına tek satır istiyorsan ürünleri toplarsın veya belirli bir sipariş satırını pencere fonksiyonlarıyla seçersin.
1. Müşteri ve ürün başına toplam tutarı hesaplamak
SELECT musteri_id,
urun,
SUM(tutar) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id, urun
ORDER BY musteri_id, urun;
Bu sorgu, aynı müşterinin farklı ürünlerini ayrı satırlarda gösterir. Örnek sonuç biçimi 10 | Defter | 120, 10 | Kalem | 80 ve 20 | Mouse | 250 şeklindedir. Burada sonuç düzeyi müşteri ve üründür; müşteri başına tek satır korunmaz.
2. Müşteri başına tek satırda ürün listesini göstermek
SELECT musteri_id,
STRING_AGG(urun, ', ' ORDER BY urun) AS urunler,
SUM(tutar) AS toplam_tutar
FROM siparisler
GROUP BY musteri_id
ORDER BY musteri_id;
STRING_AGG, gruptaki metin değerlerini tek bir metinde birleştiren bir toplama fonksiyonudur. Örnek sonuç 10 | Defter, Kalem | 200 ve 20 | Mouse | 250 biçimindedir. Ürünlerden yalnızca birini göstermek için MIN veya MAX kullanmak teknik olarak mümkün olsa da seçilen ürünün iş açısından anlamlı olduğundan emin olmalısın.
3. Müşteri toplamını koruyup tek sipariş satırı seçmek
WITH siparis_ozeti AS (
SELECT id,
musteri_id,
urun,
tutar,
tarih,
SUM(tutar) OVER (
PARTITION BY musteri_id
) AS musteri_toplami,
ROW_NUMBER() OVER (
PARTITION BY musteri_id
ORDER BY tarih DESC, id DESC
) AS sira
FROM siparisler
)
SELECT id,
musteri_id,
urun,
tutar,
tarih,
musteri_toplami
FROM siparis_ozeti
WHERE sira = 1
ORDER BY musteri_id;
Bu sürümde her müşteri için tarihe göre en son sipariş satırı seçilirken müşterinin toplam harcaması da gösterilir. Örneğin sonuç 10 | Kalem | 80 | 200 ve 20 | Mouse | 250 | 250 biçiminde olabilir. Sıralama ölçütünü değiştirerek en yüksek tutarlı ya da en eski siparişi seçebilirsin.
| amaç | sonuç düzeyi | tercih nedeni |
|---|---|---|
| Ürün bazında toplam almak | Müşteri ve ürün | Her ürünün toplamını ayrı görmek için |
| Ürünleri müşteri satırında birleştirmek | Müşteri | Tek satırda liste ve toplam göstermek için |
| Tek sipariş satırı ve müşteri toplamını birlikte almak | Müşteri ve seçilen sipariş | Belirli bir sıralama kuralına göre temsilci satır seçmek için |
Birincil anahtarla gruplama neden farklı davranır?

Birincil anahtar her satırı tekil olarak belirlediği için PostgreSQL, aynı tablodaki diğer sütunların bu anahtara işlevsel olarak bağlı olduğunu kabul edebilir. Bu nedenle id birincil anahtarsa aşağıdaki sorguda diğer sütunları ayrıca GROUP BY içine yazmak gerekmeyebilir:
SELECT id,
musteri_id,
urun,
tutar,
tarih
FROM siparisler
GROUP BY id
ORDER BY id;
Bir müşterinin birden fazla sipariş satırı olabileceği için GROUP BY musteri_id aynı sonucu vermez. Örneğin 10 numaralı müşterinin iki siparişi varsa bu iki satır tek grupta birleşir ve PostgreSQL hangi urun değerinin gösterileceğini kendiliğinden seçemez.
PostgreSQL belgeleri, gruplama sütunlarının ungrouped sütunun bulunduğu tablonun birincil anahtarı olması durumunda bu işlevsel bağımlılığı tanır. Bu kapsamı PostgreSQL SELECT belgelerinde görebilirsin. Birleştirmeler, alt sorgular veya farklı tablolardan gelen sütunlar kullanıldığında aynı kolaylığın otomatik olarak geçerli olduğunu varsaymamalısın.
Her müşteriden tam bir satır nasıl seçilir?
Her müşterinin tüm sütunlarıyla birlikte yalnızca son siparişini istiyorsan GROUP BY tek başına uygun araç olmayabilir. Çünkü GROUP BY grupları tek satıra indirirken hangi sipariş satırının seçileceğini kendiliğinden belirlemez.
PostgreSQL’e özgü DISTINCT ON, her müşteri grubunda sıralama bakımından ilk gelen satırı seçmek için kullanılabilir:
SELECT DISTINCT ON (musteri_id)
id,
musteri_id,
tarih,
tutar
FROM Siparisler
ORDER BY musteri_id, tarih DESC, id DESC;
Bu sorgunun amacı her müşterinin tarih bakımından en son siparişini getirmektir. Örneğin sonuçta bir müşteri için 2026-03-08 tarihli sipariş, başka bir müşteri için 2026-03-11 tarihli sipariş bulunabilir. Aynı müşterinin aynı tarihte birden fazla siparişi varsa id DESC, bu kayıtlar arasından kimlik değeri daha büyük olanı seçer. Böylece seçim ölçütü açık ve tekrarlanabilir hâle gelir. PostgreSQL SELECT belgeleri de DISTINCT ON kullanımında istenen ilk satırı belirlemek için ORDER BY yazılmasını açıklar.
Aynı işlem pencere fonksiyonuyla da yapılabilir. Pencere fonksiyonu, satırları grupları yok etmeden sıralar ve her satıra grup içindeki konumunu verir.
WITH sirali AS (
SELECT
s.*,
ROW_NUMBER() OVER (
PARTITION BY musteri_id
ORDER BY tarih DESC, id DESC
) AS sira,
SUM(tutar) OVER (
PARTITION BY musteri_id
) AS musteri_toplami
FROM Siparisler AS s
)
SELECT
id,
musteri_id,
tarih,
tutar,
musteri_toplami
FROM sirali
WHERE sira = 1
ORDER BY musteri_id;
Bu sürüm her müşterinin son siparişini seçerken aynı müşterinin tüm sipariş tutarını da musteri_toplami sütununda gösterir. Bu nedenle müşteri toplamı ile seçilen son siparişin aynı sonuçta bulunması gerektiğinde pencere fonksiyonu daha elverişli olabilir.
DISTINCT ON ve ROW_NUMBER() her durumda GROUP BY yerine kullanılmaz. Önce sonuçta bir müşteri özeti mi, yoksa müşteriye ait belirli bir sipariş satırı mı istediğini belirlemelisin.
WHERE, HAVING ve MySQL farklarını nasıl ayırırsın?
WHERE satırları gruplama yapılmadan önce filtreler, HAVING ise gruplama ve toplama işleminden sonra oluşan grupları filtreler. Bu yüzden WHERE tutar > 500 sipariş satırlarını, HAVING SUM(tutar) > 2000 ise müşteri toplamlarını sınırlar.
SELECT
musteri_id,
SUM(tutar) AS toplam_tutar
FROM Siparisler
WHERE tutar > 500
GROUP BY musteri_id
HAVING SUM(tutar) > 2000;
Bu sorguda önce tutarı 500’den büyük siparişler alınır, ardından müşteri bazında toplam hesaplanır ve toplamı 2000’i aşan müşteriler bırakılır.
Hatayı gidermek için SELECT listesindeki her sütunu GROUP BY içine eklemek sorguyu teknik olarak çalıştırabilir, ancak sonuç düzeyini değiştirebilir. Örneğin id, tarih ve tutar da gruplamaya eklenirse her sipariş ayrı bir gruba dönüşebilir ve müşteri toplamı yerine sipariş düzeyinde sonuç alırsın.
DISTINCT, aynı değerlerden oluşan tekrar eden sonuç satırlarını ayıklamaya yarar. GROUP BY ise gruplar oluşturur ve bu gruplar üzerinde SUM, COUNT veya MAX gibi toplama işlemleri yapmana imkân verir.
MySQL’de ONLY_FULL_GROUP_BY etkin olduğunda, toplama fonksiyonu kullanılmayan sütunların GROUP BY içinde bulunması veya gruplama sütunları tarafından işlevsel olarak belirlenmesi gerekir. MySQL belgelerinde bu modun varsayılan SQL modu içinde yer aldığı ve işlevsel bağımlılığın sorgu kabulünde değerlendirildiği belirtilir. Modun kapatıldığı eski veya özel yapılandırmalarda çalışan bir sorgu, PostgreSQL’in daha sıkı değerlendirmesinde hata verebilir.
- Sonuçta bir satırın neyi temsil edeceğini belirle.
SELECTlistesindeki sütunların gruplu, toplama fonksiyonlu veya işlevsel olarak belirlenmiş olup olmadığını kontrol et.- Satırları mı, grupları mı filtreleyeceğine karar ver.
- Toplam, sayım veya en yüksek değer gibi bir hesap gerekip gerekmediğini incele.
- Her gruptan tam bir satır seçilecekse
DISTINCT ONya da pencere fonksiyonlarını değerlendir.
Sık Sorulan Sorular
GROUP BY’a SELECT listesindeki bütün sütunları eklemek hatayı her zaman doğru çözer mi?
GROUP BY’a bütün sütunları eklemek sözdizimi hatasını giderebilir, ancak her sütun gruplama düzeyini daraltacağı için toplam müşteri özeti yerine sipariş bazında sonuç üretebilir.
PostgreSQL’de birincil anahtar ile GROUP BY yaptığımda diğer sütunları neden seçebiliyorum?
PostgreSQL, gruplama sütunu aynı tablonun birincil anahtarı olduğunda diğer sütunların o anahtar tarafından işlevsel olarak belirlendiğini kabul edebilir. Birincil anahtar her satırı tekil belirlediği için grupta ilgili sütunların tek bir değeri bulunur.
Her müşterinin son siparişini GROUP BY ile mi seçmeliyim?
Her müşterinin son siparişinin tüm sütunlarını seçmek için GROUP BY kullanmak zorunda değilsin; PostgreSQL’de DISTINCT ON veya ROW_NUMBER() ile tarih ve id sıralamasını açıkça tanımlayabilirsin.
WHERE ile HAVING arasındaki fark nedir?
WHERE gruplama öncesinde sipariş satırlarını filtreler, HAVING ise GROUP BY sonrasında oluşan müşteri gruplarını veya hesaplanan toplamları filtreler.
MySQL’de çalışan GROUP BY sorgusu PostgreSQL’de neden hata verir?
MySQL sorgusu farklı bir SQL modu, işlevsel bağımlılık değerlendirmesi veya sorgu yapısı sayesinde kabul edilmiş olabilir. PostgreSQL ise seçilen ve toplama yapılmayan sütunun gruplama ilişkisini açıkça kurmanı isteyebilir.
Bu hatayı çözmenin en güvenilir yolu, önce sorgunun sonuç düzeyini tanımlamak, ardından her sütunun bu sonuçta hangi kuralla yer aldığını açıkça göstermektir.