1. İndeksleri Etkili Kullanın
Senaryo: Büyük bir müşteri tablosunda sık sık isim araması yapılıyor. Açıklama: İsim sütununa indeks ekleyerek aramaları hızlandırabilirsiniz.
CREATE INDEX idx_customer_name ON customers(name);
2. EXPLAIN Planını Analiz Edin
Senaryo: Yavaş çalışan bir sorgunuz var ve nedenini anlamak istiyorsunuz. Açıklama: EXPLAIN komutu ile sorgu planını inceleyerek, sorgunun nasıl çalıştığını ve olası darboğazları görebilirsiniz.
EXPLAIN SELECT * FROM orders WHERE customer_id = 1000;
3. Gereksiz Sütunları Seçmekten Kaçının
Senaryo: Tüm ürün bilgilerini çekmek yerine sadece ad ve fiyat bilgisi gerekiyor. Açıklama: SELECT * yerine sadece ihtiyaç duyulan sütunları seçin.
-- Yerine: SELECT name, price FROM products; -- Bundan kaçının: SELECT * FROM products;
4. WHERE Koşullarını Optimize Edin
Senaryo: Tarih aralığında sipariş arama yapılıyor. Açıklama: İndekslenmiş sütunlar üzerinde eşitlik koşulları kullanın ve fonksiyon kullanımından kaçının.
-- İyi: WHERE order_date BETWEEN '2023-01-01' AND '2023-12-31' -- Kötü: WHERE YEAR(order_date) = 2023
5. LIKE Yerine Full-Text Arama Kullanın
Senaryo: Ürün açıklamalarında kelime araması yapılıyor. Açıklama: Büyük metin alanlarında LIKE '%kelime%' yerine full-text arama kullanın.
CREATE FULLTEXT INDEX idx_product_description ON products(description); SELECT * FROM products WHERE MATCH(description) AGAINST('aranan kelime');
6. Geçici Tablolar Yerine CTE Kullanın
Senaryo: Karmaşık bir rapor sorgusu birkaç ara sonuç gerektiriyor. Açıklama: Geçici tablolar yerine CTE (Common Table Expressions) kullanarak sorgu okunabilirliğini ve performansını artırın.
WITH monthly_sales AS ( SELECT DATE_TRUNC('month', order_date) AS month, SUM(total) AS total_sales FROM orders GROUP BY DATE_TRUNC('month', order_date) ) SELECT * FROM monthly_sales WHERE total_sales > 10000;
7. Partition Tabloları Kullanın
Senaryo: Çok büyük bir log tablosunda yıllara göre sorgulama yapılıyor. Açıklama: Tabloyu yıllara göre bölümleyerek (partitioning) sorgu performansını artırın.
CREATE TABLE logs ( id INT, log_date DATE, message TEXT ) PARTITION BY RANGE (EXTRACT(YEAR FROM log_date)); CREATE TABLE logs_2022 PARTITION OF logs FOR VALUES FROM (2022) TO (2023); CREATE TABLE logs_2023 PARTITION OF logs FOR VALUES FROM (2023) TO (2024);
8. Doğru Veri Tiplerini Kullanın
Senaryo: Telefon numaraları için VARCHAR kullanılıyor. Açıklama: Sayısal değerler için uygun sayısal veri tiplerini kullanın. Örneğin, telefon numaraları için BIGINT kullanın.
ALTER TABLE customers ALTER COLUMN phone_number TYPE BIGINT;
9. IN Yerine EXISTS Kullanın
Senaryo: Belirli ürün kategorilerine ait ürünleri listeliyorsunuz. Açıklama: Büyük listelerde IN yerine EXISTS kullanmak daha performanslı olabilir.
-- Yerine: SELECT * FROM products p WHERE EXISTS (SELECT 1 FROM categories c WHERE c.id = p.category_id AND c.name IN ('Electronics', 'Books')); -- Bundan kaçının: SELECT * FROM products WHERE category_id IN (SELECT id FROM categories WHERE name IN ('Electronics', 'Books'));
10. Düzenli İstatistik Güncellemeleri Yapın
Senaryo: Sorgu planlayıcısı optimal planı üretmiyor. Açıklama: Veritabanı istatistiklerini düzenli olarak güncelleyerek sorgu planlayıcısının daha iyi kararlar vermesini sağlayın.
ANALYZE customers;
11. Birleştirme (JOIN) İşlemlerini Optimize Edin
Senaryo: Çok sayıda tabloyu birleştiren karmaşık bir sorgunuz var. Açıklama: Birleştirme sırasını optimize edin, en küçük sonuç kümesini üreten tabloları önce birleştirin.
SELECT c.name, o.order_date, p.product_name FROM customers c JOIN orders o ON c.id = o.customer_id JOIN order_items oi ON o.id = oi.order_id JOIN products p ON oi.product_id = p.id WHERE c.country = 'USA'
12. Gereksiz Alt Sorguları Ortadan Kaldırın
Senaryo: Her müşterinin en son siparişini bulmak için alt sorgu kullanılıyor. Açıklama: Alt sorgu yerine analitik fonksiyonlar kullanarak performansı artırın.
-- Yerine: SELECT customer_id, order_date, total FROM ( SELECT customer_id, order_date, total, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) as rn FROM orders ) sub WHERE rn = 1; -- Bundan kaçının: SELECT customer_id, order_date, total FROM orders o1 WHERE order_date = ( SELECT MAX(order_date) FROM orders o2 WHERE o2.customer_id = o1.customer_id );
13. İndeks Taraması Yerine Tablo Taramasından Kaçının
Senaryo: Büyük bir tabloda belirli bir koşulu sağlayan az sayıda satır aranıyor. Açıklama: Uygun indeksler oluşturarak tablo taramasını önleyin.
CREATE INDEX idx_orders_status ON orders(status); SELECT * FROM orders WHERE status = 'shipped';
14. UNION ALL ve UNION'u Doğru Kullanın
Senaryo: İki farklı sorgu sonucunu birleştirmeniz gerekiyor. Açıklama: Tekrar eden satırları elemeniz gerekmiyorsa, UNION yerine UNION ALL kullanın.
-- Daha hızlı (tekrar eden satırları elemez): SELECT product_id FROM order_items UNION ALL SELECT product_id FROM cart_items; -- Daha yavaş (tekrar eden satırları eler): SELECT product_id FROM order_items UNION SELECT product_id FROM cart_items;
15. Gereksiz Koşulları Kaldırın
Senaryo: WHERE yan tümcesinde gereksiz veya tekrar eden koşullar var. Açıklama: Mantıksal olarak eşdeğer olan daha basit koşullar kullanın.
-- Yerine: WHERE status = 'active' AND (age >= 18 AND age <= 65) -- Bundan kaçının: WHERE status = 'active' AND age >= 18 AND age <= 65 AND status != 'inactive'
16. Sayfalama (Pagination) Kullanın
Senaryo: Büyük bir sonuç kümesinin tamamını bir kerede çekiyorsunuz. Açıklama: LIMIT ve OFFSET kullanarak sonuçları sayfalara bölün.
SELECT * FROM products ORDER BY name LIMIT 20 OFFSET 40; -- 3. sayfa, sayfa başına 20 ürün
17. Koşullu Toplama İşlemlerini Optimize Edin
Senaryo: Farklı durumlar için ayrı ayrı toplama yapmanız gerekiyor. Açıklama: Çoklu CASE ifadeleri ile tek bir tarama yapın.
SELECT SUM(CASE WHEN status = 'completed' THEN total ELSE 0 END) as completed_total, SUM(CASE WHEN status = 'pending' THEN total ELSE 0 END) as pending_total FROM orders;
18. Önyükleme (Prefetching) Kullanın
Senaryo: Bir uygulamada, ilişkili verileri ayrı sorgularla çekiyorsunuz. Açıklama: İlişkili verileri tek bir sorguda önceden yükleyin.
SELECT o.*, c.name as customer_name, c.email as customer_email FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.status = 'processing';
19. Geçici İndeksler Kullanın
Senaryo: Nadiren çalıştırılan ama yoğun işlem gerektiren bir raporlama sorgunuz var. Açıklama: Sorgu öncesinde geçici indeks oluşturup, sorgu sonrasında silin.
CREATE INDEX tmp_idx_order_date ON orders(order_date); -- Raporlama sorgusunu çalıştır DROP INDEX tmp_idx_order_date;
20. Dış Anahtarları (Foreign Keys) Akıllıca Kullanın
Senaryo: Çok sayıda ilişkili tablo var ve veri bütünlüğünü korumak istiyorsunuz. Açıklama: Dış anahtarlar veri bütünlüğünü sağlar ancak performansı etkileyebilir. Gerektiğinde uygulama seviyesinde kontroller yapın.
ALTER TABLE orders ADD CONSTRAINT fk_customer FOREIGN KEY (customer_id) REFERENCES customers(id);