SQL ile Raporları Hızlandırmak: İndeksler ve Sorgu İpuçları
Yavaş çalışan SQL raporları can sıkıcı olabilir. İndeksler ve akıllı sorgu teknikleriyle performansı dramatik biçimde artırmanın yollarını keşfedelim.
Bir rapor sayfası açıyorsunuz, ekran donuyor, dakikalarca bekliyorsunuz. Tanıdık geldi mi? Yavaş SQL sorguları sadece sinir bozucu değil; üretim sistemlerinde gerçek bir maliyet kalemi. Bu yazıda raporlarınızı hızlandırmak için kullandığım pratik teknikleri paylaşacağım.
Sorunun Kaynağını Bulmak: EXPLAIN Planı
Her şeyden önce, "neden yavaş?" sorusunu yanıtlamak gerekiyor. Bunun için en güçlü silah EXPLAIN (veya SQL Server'da SET STATISTICS IO ON) komutudur.
EXPLAIN ANALYZE
SELECT * FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE o.created_at > '2024-01-01';
Çıktıda Seq Scan (sıralı tarama) görüyorsanız alarm zilleri çalmalı. Bu, veritabanının tablonun tamamını satır satır taradığı anlamına gelir; büyük tablolarda bu felakettir.
İndeksler: Doğru Yerde, Doğru Türde
İndeks koymak iyidir, ama her yere indeks koymak daha da kötü olabilir. İşte dikkat ettiğim kurallar:
Hangi Sütunlara İndeks Açılmalı?
WHEREkoşullarında sık kullanılan sütunlarJOINanahtarları (foreign keysütunları)ORDER BYveGROUP BYiçinde geçen sütunlar- Yüksek kardinaliteye sahip sütunlar (cinsiyet gibi yalnızca 2 değer alan sütunlara indeks genellikle işe yaramaz)
Bileşik (Composite) İndeks Sırası Önemli
-- Yanlış sıra: filtreleme önce status, sonra date ise
CREATE INDEX idx_orders ON orders (status, created_at);
-- Doğru sıra: en seçici sütun öne
CREATE INDEX idx_orders ON orders (created_at, status);
Bileşik indekste sütun sırası, kitaptaki dizin mantığıyla aynı: önce genel, sonra özel.
Kapsayıcı (Covering) İndeks
Sorgunun ihtiyaç duyduğu tüm sütunları indekse dahil ederseniz, veritabanı asıl tabloya hiç dokunmaz:
CREATE INDEX idx_covering ON orders (created_at)
INCLUDE (customer_id, total_amount, status);
Bu teknik özellikle yoğun raporlama sorgularında muazzam fark yaratır.
Sorgu Yazım İpuçları
İndeks ne kadar iyi olursa olsun, sorguyu yanlış yazarsanız sizi kurtaramaz.
SELECT * Kullanmayın
Sadece ihtiyacınız olan sütunları çekin. Hem ağ trafiği azalır hem de covering indekslerden yararlanabilirsiniz.
Fonksiyonları WHERE İçinde Kullanmaktan Kaçının
-- İndeksi devre dışı bırakır ❌
WHERE YEAR(created_at) = 2024
-- İndeksi kullanır ✅
WHERE created_at BETWEEN '2024-01-01' AND '2024-12-31'
Gereksiz JOIN'lerden Kurtulun
Raporda kullanılmayan tablolara JOIN atmayın. Her ek JOIN, sorgu planını karmaşıklaştırır.
WITH (CTE) ile Okunabilirlik + Performans
Karmaşık raporları CTE ile parçalara bölmek hem okunabilirliği artırır hem de optimizer'ın daha iyi plan üretmesine yardımcı olabilir:
WITH monthly_sales AS (
SELECT DATE_TRUNC('month', created_at) AS month,
SUM(total_amount) AS revenue
FROM orders
WHERE status = 'completed'
GROUP BY 1
)
SELECT * FROM monthly_sales ORDER BY month;
İstatistikleri Güncel Tutun
Veritabanı, sorgu planını istatistiklere göre üretir. Tablolar büyüdükçe istatistikler eskir ve optimizer yanlış kararlar alır. Periyodik olarak:
ANALYZE orders; -- PostgreSQL
-- veya
UPDATE STATISTICS orders; -- SQL Server
komutlarını çalıştırmayı alışkanlık haline getirin.
Sonuç
SQL performansı bir kez yapıp unutulan bir iş değil. Veriler büyüdükçe, sorgular değiştikçe yeniden gözden geçirmek gerekiyor. Benim rutenim: önce EXPLAIN ile darboğazı bul, sonra doğru indeksi ekle, son olarak sorguyu sadeleştir. Bu üç adım, vaka vaka çalışıyor.
Sizin en çok hangi sorgu tipi başınızı ağrıtıyor? Yorumlarda buluşalım.