MySQL’deki ONLY_FULL_GROUP_BY hatasının tercih edilen çözümü modu kapatmak değil, seçilen her toplulaştırılmamış ifadenin GROUP BY sütunlarına işlevsel olarak bağlı olmasını sağlamak veya o ifade için bilinçli bir aggregate fonksiyonu kullanmaktır. Böylece her grubun hangi değeri üreteceği açık ve tekrarlanabilir olur.
Karşılaşılan hata çoğunlukla şu biçimdedir:
Expression #2 of SELECT list is not in GROUP BY clause and contains
nonaggregated column ... which is not functionally dependent on columns
in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Hata neden oluşur?
GROUP BY, birden fazla satırı tek sonuç satırında birleştirir. Bir grupta farklı değerler taşıyabilen bir sütunu ne gruplama anahtarına ekler ne de aggregate fonksiyonuyla nasıl seçileceğini belirtirseniz, veritabanının hangi değeri döndürmesi gerektiği belirsiz kalır.
MySQL, ONLY_FULL_GROUP_BY etkinken SELECT, HAVING ve ORDER BY bölümlerindeki toplulaştırılmamış ifadeleri denetler. Bir ifade gruplama sütunlarında değilse ve bu sütunlara işlevsel olarak bağlı değilse sorguyu reddeder. Birincil anahtar veya UNIQUE NOT NULL kısıtı gibi şema özellikleri MySQL’in işlevsel bağımlılığı tanımasını sağlayabilir.
Hatayı yeniden üretme
Aşağıdaki geçici tablo, aynı kullanıcı için farklı müşteri adları içeren küçük bir örnek oluşturur:
CREATE TEMPORARY TABLE orders (
id INT PRIMARY KEY,
user_id INT NOT NULL,
customer_name VARCHAR(100) NOT NULL,
amount DECIMAL(10, 2) NOT NULL
);
INSERT INTO orders (id, user_id, customer_name, amount) VALUES
(1, 10, 'Ayşe', 120.00),
(2, 10, 'Ayşe A.', 80.00),
(3, 20, 'Mehmet', 50.00);
Bu sorguda customer_name, ne gruplama anahtarıdır ne de user_id tarafından benzersiz biçimde belirlenir:
SELECT user_id, customer_name, COUNT(*) AS order_count
FROM orders
GROUP BY user_id;
user_id = 10 grubu için hem Ayşe hem de Ayşe A. bulunduğundan tek bir customer_name seçmek belirsizdir.
Sorguyu amaca göre düzeltme
1. Yalnızca grup özetine ihtiyacınız varsa sütunu kaldırın
Müşteri adı sonuç için gerekli değilse en temiz çözüm onu seçmemektir:
SELECT
user_id,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY user_id;
Bu sorgu her kullanıcı için sipariş sayısını ve toplam tutarı deterministik biçimde üretir.
2. Her farklı değer ayrı grup olmalıysa sütunu GROUP BY’a ekleyin
İstenen sonuç kullanıcı ve müşteri adı birleşimine göre bir satırsa bunu açıkça yazın:
SELECT
user_id,
customer_name,
COUNT(*) AS order_count,
SUM(amount) AS total_amount
FROM orders
GROUP BY user_id, customer_name;
Bu değişiklik sonuç kümesinin anlamını değiştirir: aynı kullanıcı birden fazla ad taşıyorsa birden fazla satır döner.
3. Belirli bir seçim kuralınız varsa aggregate kullanın
Gerçek iş kuralı alfabetik olarak en büyük adı seçmekse MAX() bunu açık ve tekrarlanabilir hale getirir:
SELECT
user_id,
MAX(customer_name) AS alphabetically_last_name,
COUNT(*) AS order_count
FROM orders
GROUP BY user_id;
MAX(customer_name) yalnızca gerçekten bu seçim kuralını istiyorsanız doğrudur. Örneğin “en son kullanılan müşteri adı” ihtiyacı için tarih veya artan kimlik üzerinden ilgili satırı bulan ayrı bir sorgu gerekir; MAX() bu anlamı kendiliğinden sağlamaz.
4. Rastgele bir grup değeri kabul edilebiliyorsa ANY_VALUE kullanın
Verinin uygulama kuralı gereği aynı olduğunu biliyor, ancak MySQL bunu şemadan çıkaramıyorsa ANY_VALUE() ile belirli bir değerin önemli olmadığını açıkça belirtebilirsiniz:
SELECT
user_id,
ANY_VALUE(customer_name) AS sample_name,
COUNT(*) AS order_count
FROM orders
GROUP BY user_id;
ANY_VALUE() deterministik bir seçim kuralı değildir. Her gruptan herhangi bir değeri kabul ettiğinizi söyler; “ilk”, “son” veya “en güncel” satırı garanti etmez.
İşlevsel bağımlılık örneği
Müşteri bilgisi ayrı tabloda ve id birincil anahtarıyla tutuluyorsa bir müşteri kimliği yalnızca bir adı belirler:
SELECT
c.id,
c.name,
COUNT(*) AS order_count,
SUM(o.amount) AS total_amount
FROM customers AS c
JOIN orders AS o ON o.user_id = c.id
GROUP BY c.id;
MySQL desteklenen durumlarda c.name ifadesinin c.id birincil anahtarına işlevsel olarak bağlı olduğunu algılar. Şema bu ilişkiyi gerçekten garanti etmiyorsa eksik kısıtı düzeltmek, sorguya rastgele bir aggregate eklemekten daha doğru olabilir.
sql_mode’u geçici olarak kapatmak
Önce oturumdaki etkin modları inceleyin:
SELECT @@SESSION.sql_mode;
Eski bir sorgunun hata kaynağını doğrulamak için ONLY_FULL_GROUP_BY yalnızca mevcut bağlantıda geçici olarak çıkarılabilir:
SET SESSION sql_mode = sys.list_drop(
@@SESSION.sql_mode,
'ONLY_FULL_GROUP_BY'
);
Bu ayar yalnızca mevcut oturumu etkiler. Bağlantı kapanınca yeni oturum sunucu varsayılanını kullanır. Global ayarı veya yapılandırma dosyasını değiştirmek uygulamadaki belirsiz sorguları gizleyebileceği için üretim çözümü olarak önerilmez; önce sorgu ve şema düzeltilmelidir.
Kontrol listesi
- Hata mesajındaki ifadeyi ve gruplama sütunlarını belirleyin.
- İfadenin sonuçta gerçekten gerekli olup olmadığını sorun.
- Ayrı grup gerekiyorsa ifadeyi
GROUP BYbölümüne ekleyin. - Tek değer gerekiyorsa iş kuralına uygun
SUM,COUNT,MINveyaMAXgibi bir aggregate seçin. - İşlevsel bağımlılık bekliyorsanız bunu birincil anahtar veya uygun benzersiz kısıtla şemada güvenceye alın.
ANY_VALUE()kullanıyorsanız herhangi bir değerin gerçekten kabul edilebilir olduğunu belgeleyin.