Birden Fazla JOIN Birleştirme (3-4 Tablo)
Giriş — Gerçek Dünyada Çoklu JOIN
Gerçek uygulamalarda neredeyse her sorgu 3-5 tablo arasında JOIN gerektirir. Bir sipariş detay sayfası: müşteri bilgisi + sipariş + sipariş kalemleri + ürünler + kategoriler = 5 tablo. Bu derste çoklu JOIN'lerin nasıl yapılandırılacağını, okunabilirlik ve performans ipuçlarını göreceğiz.
Zincirleme JOIN Yapısı
-- 4 tablo: customers → orders → order_items → products
SELECT
c.first_name,
c.last_name,
o.order_id,
o.order_date,
p.product_name,
oi.quantity,
oi.unit_price,
oi.quantity * oi.unit_price AS line_total
FROM customers c -- Başlangıç tablosu
INNER JOIN orders o ON c.customer_id = o.customer_id -- Müşterinin siparişleri
INNER JOIN order_items oi ON o.order_id = oi.order_id -- Siparişin kalemleri
INNER JOIN products p ON oi.product_id = p.product_id -- Kalemlerin ürün bilgisi
WHERE o.status = 'delivered'
ORDER BY o.order_date DESC, o.order_id, p.product_name;JOIN Zinciri:
customers ──(customer_id)──► orders ──(order_id)──► order_items ──(product_id)──► productsINNER ve LEFT JOIN'i Karıştırma
-- Tüm ürünler, kategorileri ve (varsa) satış bilgileri
SELECT
p.product_name,
c.category_name,
COALESCE(SUM(oi.quantity), 0) AS total_sold,
COALESCE(ROUND(SUM(oi.quantity * oi.unit_price), 0), 0) AS revenue
FROM products p
INNER JOIN categories c ON p.category_id = c.category_id -- Kategorisi olmayan ürün yok
LEFT JOIN order_items oi ON p.product_id = oi.product_id -- Satılmamış olabilir
LEFT JOIN orders o ON oi.order_id = o.order_id -- Sipariş bilgisi
AND o.status != 'cancelled' -- İptal edilmemiş
WHERE p.is_active = TRUE
GROUP BY p.product_id, p.product_name, c.category_name
ORDER BY revenue DESC;Burada INNER JOIN ile LEFT JOIN birlikte kullanılıyor:
products → categories: INNER (her ürünün kategorisi var)products → order_items: LEFT (satılmamış ürünler de görünmeli)order_items → orders: LEFT (satılmamışta sipariş olmaz)
5 Tablo JOIN — E-Ticaret Detay Raporu
-- Tam sipariş raporu: müşteri + sipariş + kalemler + ürünler + kategoriler
SELECT
CONCAT(c.first_name, ' ', c.last_name) AS musteri,
c.city,
o.order_id,
DATE_FORMAT(o.order_date, '%d.%m.%Y') AS tarih,
o.status,
cat.category_name AS kategori,
p.product_name AS urun,
oi.quantity AS adet,
CONCAT('₺', FORMAT(oi.unit_price, 2)) AS birim_fiyat,
CONCAT('₺', FORMAT(oi.quantity * oi.unit_price, 2)) AS satir_toplam
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_items oi ON o.order_id = oi.order_id
INNER JOIN products p ON oi.product_id = p.product_id
LEFT JOIN categories cat ON p.category_id = cat.category_id
ORDER BY o.order_date DESC, o.order_id;JOIN Sırası ve Performans
MySQL optimizer genellikle JOIN sırasını optimize eder, ama bazı ipuçları:
Küçük tablolar → büyük tablolar yönünde JOIN yap
Filtreleme yapılan tabloyu (WHERE koşulu olan) daha önce JOIN'le
INDEX olan sütunlarla JOIN yap
-- WHERE koşulunu erken uygula
SELECT p.product_name, oi.quantity
FROM orders o -- WHERE filtresi bu tabloda
INNER JOIN order_items oi ON o.order_id = oi.order_id
INNER JOIN products p ON oi.product_id = p.product_id
WHERE o.status = 'delivered' AND o.order_date > '2024-01-01';
-- MySQL optimizer WHERE'i erken uygularOkunabilirlik İpuçları
-- ✅ İyi: Her JOIN ayrı satırda, alias anlamlı, girintili
SELECT
c.first_name,
o.order_id,
p.product_name
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id
INNER JOIN order_items oi ON o.order_id = oi.order_id
INNER JOIN products p ON oi.product_id = p.product_id;
-- ❌ Kötü: Tek satırda, alias anlamsız
SELECT a.first_name, b.order_id, d.product_name FROM customers a INNER JOIN orders b ON a.customer_id=b.customer_id INNER JOIN order_items c ON b.order_id=c.order_id INNER JOIN products d ON c.product_id=d.product_id;Alias kuralları:
c= customers,o= orders,oi= order_items,p= productsTablo adının ilk harfi veya kısaltması
Anlamlı ve tutarlı olsun
Gerçek Dünya Örneği — Dashboard KPI Sorgusu
-- Admin dashboard: müşteri-bazlı özet
SELECT
c.customer_id,
CONCAT(c.first_name, ' ', c.last_name) AS customer,
c.city,
COUNT(DISTINCT o.order_id) AS orders,
COUNT(oi.item_id) AS items_purchased,
ROUND(SUM(oi.quantity * oi.unit_price), 2) AS total_revenue,
COUNT(DISTINCT p.category_id) AS categories_bought,
MAX(o.order_date) AS last_order
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id AND o.status != 'cancelled'
LEFT JOIN order_items oi ON o.order_id = oi.order_id
LEFT JOIN products p ON oi.product_id = p.product_id
GROUP BY c.customer_id, c.first_name, c.last_name, c.city
ORDER BY total_revenue DESC;Sıkça Yapılan Hatalar
JOIN koşulunu yanlış tabloya bağlamak:
order_items.product_id = customers.customer_idgibi yanlış eşleşme. Tablo ilişkilerini (FK→PK) doğru takip et.Kartezyen çarpım: Eksik ON koşulu veya yanlış JOIN satır sayısını patlatır. Sonuç beklediğinden çok fazlaysa JOIN koşullarını kontrol et.
LEFT JOIN + WHERE sağ tablo filtresi: LEFT JOIN'in anlamını bozan WHERE koşullarına dikkat et.
Gereksiz JOIN: Sonuçta kullanmadığın tabloya JOIN yapma — gereksiz performans kaybı.
Özet
Gerçek uygulamalarda 3-5 tablo JOIN yaygındır
JOIN'ler zincirleme uygulanır: A → B → C → D
INNER ve LEFT JOIN aynı sorguda karıştırılabilir
Her JOIN'in ON koşulu doğru FK-PK eşleşmesi yapmalı
Okunabilirlik için: anlamlı alias, girintili format, her JOIN ayrı satırda
Performans için: WHERE filtresini erken uygula, indexli sütunlarla JOIN yap
AI Asistan
Sorularını yanıtlamaya hazır