Subquery Best Practices ve Performans
Giriş — Doğru Aracı Doğru Yerde Kullan
Subquery güçlü bir araçtır ama her yerde kullanmak doğru değildir. Bu derste subquery kullanırken dikkat edilmesi gereken performans kurallarını, anti-pattern'leri ve optimizasyon tekniklerini göreceğiz.
Kural 1: JOIN Mümkünse JOIN Tercih Et
-- ❌ Correlated subquery — N kez çalışır
SELECT product_name,
(SELECT category_name FROM categories WHERE category_id = p.category_id) AS category
FROM products p;
-- ✅ JOIN — tek geçişte çözer
SELECT p.product_name, c.category_name
FROM products p
LEFT JOIN categories c ON p.category_id = c.category_id;Kural 2: SELECT'te Çoklu Subquery'den Kaçın
-- ❌ Her müşteri için 3 ayrı subquery = 3N sorgu
SELECT c.first_name,
(SELECT COUNT(*) FROM orders WHERE customer_id = c.customer_id) AS cnt,
(SELECT SUM(total_amount) FROM orders WHERE customer_id = c.customer_id) AS total,
(SELECT MAX(order_date) FROM orders WHERE customer_id = c.customer_id) AS last_order
FROM customers c;
-- ✅ Tek LEFT JOIN + GROUP BY = tek geçiş
SELECT c.first_name,
COUNT(o.order_id) AS cnt,
COALESCE(SUM(o.total_amount), 0) AS total,
MAX(o.order_date) AS last_order
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_id, c.first_name;Kural 3: NOT IN Yerine NOT EXISTS
-- ⚠️ NOT IN — NULL tuzağı var
SELECT * FROM customers
WHERE customer_id NOT IN (SELECT customer_id FROM orders);
-- Eğer orders'ta customer_id NULL varsa → boş sonuç!
-- ✅ NOT EXISTS — güvenli ve genellikle daha hızlı
SELECT * FROM customers c
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);Kural 4: Subquery'de Index Kullanımına Dikkat Et
-- Subquery sonucunda index yoktur — büyük sonuçlarda yavaş
WHERE customer_id IN (SELECT customer_id FROM large_temp_table);
-- large_temp_table'da index yoksa full scan
-- Çözüm: Geçici tablo oluştur ve index ekle, veya JOIN kullanKural 5: Derived Table'ı Materialized Etme
MySQL 8.0'da optimizer derived table'ı bazen materialize eder (geçici tablo oluşturur) bazen merge eder (dış sorguya birleştirir). EXPLAIN ile kontrol et:
EXPLAIN SELECT * FROM (
SELECT category_id, AVG(price) AS avg_price
FROM products GROUP BY category_id
) AS cat_avg WHERE avg_price > 1000;Subquery vs JOIN Karar Ağacı
İki tablodan sütun gerekiyor mu?
├── EVET → JOIN
└── HAYIR
├── Varlık/yokluk kontrolü mü?
│ ├── EVET → EXISTS / NOT EXISTS
│ └── HAYIR
│ ├── Aggregate karşılaştırma mı?
│ │ ├── EVET → Subquery (WHERE'da)
│ │ └── HAYIR → JOIN veya IN
│ └── Ara hesaplama gerekli mi?
│ ├── EVET → Derived table veya CTE
│ └── HAYIR → En basit yöntemi seçEXPLAIN ile Subquery Performansını Kontrol Et
EXPLAIN SELECT product_name FROM products
WHERE category_id IN (SELECT category_id FROM categories WHERE parent_category_id = 1);
-- type: eq_ref → iyi (index kullanılıyor)
-- type: ALL → kötü (full table scan)
-- Extra: "Using subquery" → subquery materialize edildiÖzet
JOIN mümkünse subquery yerine JOIN kullan
SELECT'te çoklu correlated subquery yerine LEFT JOIN + GROUP BY
NOT IN yerine NOT EXISTS (NULL-safe, genellikle daha hızlı)
Derived table'lar karmaşık hesaplamalar için iyi ama CTE daha okunabilir
EXPLAIN ile performansı kontrol et
Modern MySQL optimizer birçok subquery'yi otomatik optimize eder ama yine de doğru yazmak önemli
AI Asistan
Sorularını yanıtlamaya hazır