← Kursa Dön
📄 Text · 35 min

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)──► products

INNER 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ı:

  1. Küçük tablolar → büyük tablolar yönünde JOIN yap

  2. Filtreleme yapılan tabloyu (WHERE koşulu olan) daha önce JOIN'le

  3. 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 uygular

Okunabilirlik İ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 = products

  • Tablo 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

  1. JOIN koşulunu yanlış tabloya bağlamak: order_items.product_id = customers.customer_id gibi yanlış eşleşme. Tablo ilişkilerini (FK→PK) doğru takip et.

  2. 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.

  3. LEFT JOIN + WHERE sağ tablo filtresi: LEFT JOIN'in anlamını bozan WHERE koşullarına dikkat et.

  4. 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