SQL ile Raporları Hızlandırmak: İndeksler ve Sorgu İpuçları
Yavaş çalışan SQL raporları iş süreçlerini sekteye uğratır. İndeksler ve akıllı sorgu teknikleriyle performansı dramatik biçimde artırın.
Sabah toplantısına girerken ekranın karşısında beklediğiniz rapor hâlâ yükleniyorsa, o sinirin asıl sorumlusu büyük ihtimalle veritabanı katmanında saklıdır. Yıllar içinde büyüyen tablolar, gelişigüzel yazılmış sorgular ve es geçilen indeksler bir araya gelince raporlar dakikalarca, hatta onlarca dakika koşabilir hale gelir. Bu yazıda pratik bir perspektiften SQL performansını nasıl iyileştirebileceğimizi ele alacağım.
Sorun Nerede Başlıyor?
Her şey veri hacmiyle büyür. Birkaç bin satırlık tabloda gözle görülmeyen bir sorun, milyonluk kayıtlara ulaşınca aniden su yüzüne çıkar. Sık karşılaştığım başlıca nedenler şunlar:
- Full table scan: Sorgu, filtreleme için uygun bir indeks bulamayıp tüm tabloyu tarar.
- Gereksiz sütun çekimi:
SELECT *kullanımı, ihtiyaç duyulmayan yüzlerce sütunu belleğe taşır. - Kötü JOIN sıralaması: Büyük tablolar küçüklerden önce birleştirilince ara sonuç seti şişer.
- Fonksiyon sarmalı filtreler:
WHERE YEAR(created_at) = 2024gibi ifadeler indeksi devre dışı bırakır.
İndeksler: Doğru Kullanıldığında Mucize Gibi
İndeks, veritabanının "arama kılavuzu"dur. Ama her sütuna kör kör indeks açmak da sorunu çözmez; yazma performansını düşürür.
B-Tree İndeksleri — Aralık sorgularında (BETWEEN, >, <) ve eşitlik kontrollerinde güçlüdür. Raporlarda tarih aralığı filtrelemesi çok yaygın olduğundan tarih sütunları için neredeyse zorunludur.
Composite (Bileşik) İndeksler — Birden fazla sütunu kapsayan sorgular için tek tek indeks açmak yerine bileşik indeks tercih edin. Sütun sırası kritiktir; en seçici sütunu öne alın.
-- Yavaş: her iki sütunda ayrı ayrı indeks
SELECT * FROM orders WHERE customer_id = 42 AND status = 'completed';
-- Hızlı: bileşik indeks
CREATE INDEX idx_orders_customer_status ON orders (customer_id, status);
Covering Index — Sorgunun ihtiyaç duyduğu tüm sütunları indekse dahil ederseniz veritabanı asıl tabloya hiç dokunmaz.
CREATE INDEX idx_covering ON orders (customer_id, status, total_amount, created_at);
Sorgu Yazarken Dikkat Edilmesi Gerekenler
*1. SELECT Yerine Sadece Lazım Olanı Çekin**
Network trafiğini ve bellek tüketimini azaltmanın en kolay yolu budur.
2. Filtrelerde Sütunu Çıplak Bırakın
-- Kötü: indeks kullanılmaz
WHERE CAST(created_at AS DATE) = '2024-01-01'
-- İyi: indeks devreye girer
WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02'
3. EXISTS vs IN
Alt sorgularda IN yerine EXISTS kullanmak büyük veri setlerinde belirgin hız farkı yaratabilir.
4. CTE ve Geçici Tablolar
Karmaşık raporlarda tekrar hesaplanan alt sorguları CTE (WITH) veya geçici tablo (#temp) ile bir kez hesaplayıp yeniden kullanın.
5. Sorgu Planını Okuyun
EXPLAIN (PostgreSQL/MySQL) veya Execution Plan (SQL Server) sizin en iyi dostunuzdur. "Full Scan" görüyorsanız duraksayın ve indeks eksikliğini araştırın.
Küçük Ama Etkili: İstatistikleri Güncel Tutun
Veritabanı motoru, hangi planı seçeceğine istatistiklere bakarak karar verir. Tablolar hızla değişiyorsa istatistikler eskiyebilir. Düzenli olarak ANALYZE (PostgreSQL) veya UPDATE STATISTICS (SQL Server) çalıştırmak sorgu planlayıcısının doğru kararlar vermesini sağlar.
Sonuç
SQL performans optimizasyonu bir kez yapılıp bırakılan bir iş değildir; veri büyüdükçe, iş gereksinimleri değiştikçe tekrar gözden geçirilmesi gerekir. Yine de doğru indeks stratejisi ve temiz sorgu alışkanlıkları ile dakikalar içinde çalışan bir raporu saniyelere indirmek mümkün — bunu defalarca yaşadım. Önce ölçün, sonra optimize edin; sezgiyle değil, veriyle ilerleyin.