← Kursa Dön
📄 Text · 30 min

IN, EXISTS, NOT EXISTS ile Subquery

Giriş — Varlık Kontrolü

"Bu müşteri sipariş vermiş mi?", "Bu ürün hiç satılmış mı?" — bu tür sorular varlık kontrolü gerektirir. IN ve EXISTS farklı yaklaşımlarla aynı sonucu üretir ama performans profilleri farklıdır.


IN vs EXISTS

-- IN: Subquery bir liste döndürür, dış sorgu o listede kontrol eder
SELECT first_name FROM customers
WHERE customer_id IN (SELECT customer_id FROM orders);

-- EXISTS: Her dış satır için subquery çalışır, TRUE/FALSE döndürür
SELECT first_name FROM customers c
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id);

İkisi de aynı sonucu verir ama:

  • IN: Subquery sonucunu bellekte tutar → küçük sonuç setlerinde hızlı

  • EXISTS: İlk eşleşmede durur → büyük tablolarda hızlı, index varsa çok verimli


NOT IN vs NOT EXISTS

-- NOT IN: Listede OLMAYAN kayıtlar
SELECT first_name FROM customers
WHERE customer_id NOT IN (
    SELECT customer_id FROM orders WHERE customer_id IS NOT NULL
);
-- ⚠️ NULL kontrolü zorunlu!

-- NOT EXISTS: Eşleşme OLMAYAN kayıtlar (NULL-safe)
SELECT first_name FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id
);
-- ✅ NULL sorunu yok

💡 İpucu: "Olmayanları bulma" senaryolarında NOT EXISTS tercih et: - NULL-safe (NOT IN'in NULL tuzağı yok) - Index varsa genellikle daha hızlı - Okunabilir: "var olmayan kayıtlar" mantığını doğrudan ifade eder


Performans Karşılaştırması

SenaryoINEXISTS
Subquery az satır döndürüyor✅ Hızlı✅ Hızlı
Subquery çok satır döndürüyor⚠️ Bellek sorunu✅ Hızlı (erken çıkış)
NOT versiyonu + NULL olasılığı⚠️ Tuzaklı✅ Güvenli
Index var✅ Hızlı✅ Çok hızlı

Gerçek Dünya Örnekleri

-- En az bir "Elektronik" kategorisi ürünü alan müşteriler
SELECT DISTINCT c.first_name, c.last_name
FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o
    INNER JOIN order_items oi ON o.order_id = oi.order_id
    INNER JOIN products p ON oi.product_id = p.product_id
    INNER JOIN categories cat ON p.category_id = cat.category_id
    WHERE o.customer_id = c.customer_id
      AND cat.category_name = 'Elektronik'
);

-- Hiç satış yapılmamış kategoriler
SELECT category_name FROM categories cat
WHERE NOT EXISTS (
    SELECT 1 FROM products p
    INNER JOIN order_items oi ON p.product_id = oi.product_id
    WHERE p.category_id = cat.category_id
);

EXISTS İpuçları

-- EXISTS subquery'de SELECT ne döndürdüğü önemsiz:
WHERE EXISTS (SELECT 1 FROM ...)       -- ✅ Geleneksel
WHERE EXISTS (SELECT * FROM ...)       -- ✅ Çalışır (optimizer optimize eder)
WHERE EXISTS (SELECT NULL FROM ...)    -- ✅ Çalışır
-- Hepsi aynı performans — EXISTS sadece satır varlığını kontrol eder

Özet

  • IN: subquery sonuç listesi küçükse ve NULL riski yoksa kullan

  • EXISTS: büyük tablolarda, index varsa, NULL-safe senaryo için tercih et

  • NOT EXISTS > NOT IN: NULL güvenliği ve performans açısından üstün

  • EXISTS subquery'de SELECT 1 yazmak yeterli — sütun seçimi önemsiz

  • Modern MySQL optimizer IN'i EXISTS'e dönüştürebilir ama explicit yazmak daha güvenli