← Kursa Dön
📄 Text · 35 min

Karşılaştırma ve Mantık Operatörleri

Giriş — Doğru Filtrelemenin Gücü

Önceki derste WHERE'in temellerini gördük. Ama gerçek dünyada sorgular çok daha karmaşık olabiliyor: "Bu yılın ilk çeyreğinde, İstanbul veya Ankara'dan, 500 TL üstü sipariş vermiş ve hesabı aktif olan müşterilerin listesi" gibi. Bu tür sorgularda operatörleri doğru birleştirmek ve birbirine zincirleme şekilde uygulamak büyük önem taşır.

Bu derste tüm karşılaştırma ve mantık operatörlerini derinlemesine inceleyeceğiz — edge case'leri, performans ipuçlarını ve gerçek dünya senaryolarını göreceğiz.

🎯 Analoji: Operatörleri bir elek sistemi gibi düşün. Her operatör farklı boyutta bir elek. İlk elek büyük parçaları ayıklar, ikinci elek orta boyutları, üçüncü elek küçükleri... Sonunda istediğin parçalar kalır. SQL'de de WHERE koşullarını birleştirerek verini "eler" ve istediğin sonuçlara ulaşırsın.


Karşılaştırma Operatörleri — Detaylı

Eşitlik ve Eşitsizlik (=, !=, <>)

-- Tam eşleşme
SELECT * FROM customers WHERE city = 'İstanbul';

-- Eşit değil (ikisi de aynı)
SELECT * FROM customers WHERE city != 'İstanbul';
SELECT * FROM customers WHERE city <> 'İstanbul';

-- Sayısal eşitlik
SELECT * FROM products WHERE category_id = 2;

-- Boolean eşitlik
SELECT * FROM products WHERE is_active = TRUE;
-- MySQL'de TRUE = 1, FALSE = 0
SELECT * FROM products WHERE is_active = 1;  -- Aynı şey

String karşılaştırmasında büyük/küçük harf:

-- utf8mb4_unicode_ci collation'da (varsayılan):
SELECT * FROM customers WHERE city = 'istanbul';
-- 'İstanbul' satırını bulur mu? EVET — çünkü ci = case-insensitive

-- Case-sensitive arama yapmak için:
SELECT * FROM customers WHERE BINARY city = 'istanbul';
-- 'İstanbul' satırını bulur mu? HAYIR — BINARY exact match ister

Büyüklük/Küçüklük (>, <, >=, <=)

-- 10.000 TL'den pahalı ürünler
SELECT product_name, price FROM products WHERE price > 10000;

-- Stoğu 50'nin altına düşmüş ürünler (kritik stok)
SELECT product_name, stock_quantity 
FROM products 
WHERE stock_quantity < 50 AND stock_quantity > 0;

-- Bu yılın siparişleri
SELECT * FROM orders WHERE order_date >= '2024-01-01';

-- Maaşı 10.000 ve altında olan çalışanlar
SELECT first_name, salary FROM employees WHERE salary <= 10000;

Tarih karşılaştırmaları:

-- Bugünden önceki siparişler
SELECT * FROM orders WHERE order_date < NOW();

-- Son 30 gündeki siparişler
SELECT * FROM orders 
WHERE order_date >= DATE_SUB(NOW(), INTERVAL 30 DAY);

-- Belirli bir tarihten sonra kaydolan müşteriler
SELECT * FROM customers 
WHERE registration_date > '2024-01-01';

IN Operatörü — Çoklu Değer Kontrolü

IN, bir değerin belirli bir liste içinde olup olmadığını kontrol eder.

-- Üç şehirden birinde yaşayan müşteriler
SELECT first_name, last_name, city 
FROM customers 
WHERE city IN ('İstanbul', 'Ankara', 'İzmir');

-- Bu sorgu şuna eşdeğerdir:
SELECT first_name, last_name, city 
FROM customers 
WHERE city = 'İstanbul' OR city = 'Ankara' OR city = 'İzmir';
-- Ama IN çok daha okunabilir!

NOT IN — Hariç tutma:

-- Bu durumlarda olmayan siparişler
SELECT * FROM orders 
WHERE status NOT IN ('cancelled', 'delivered');

-- Belirli kategorilerin dışındaki ürünler
SELECT * FROM products 
WHERE category_id NOT IN (7, 8);

Subquery ile IN:

-- Sipariş vermiş müşterilerin listesi
SELECT first_name, last_name 
FROM customers 
WHERE customer_id IN (
    SELECT DISTINCT customer_id FROM orders
);

-- Hiç sipariş vermemiş müşteriler
SELECT first_name, last_name 
FROM customers 
WHERE customer_id NOT IN (
    SELECT DISTINCT customer_id FROM orders
);

⚠️ Dikkat: NOT IN ile subquery kullanırken, subquery'den NULL döndürülürse sonuç boş olabilir! Çünkü x NOT IN (1, 2, NULL) her zaman UNKNOWN döner. Subquery'de NULL olma ihtimali varsa NOT EXISTS kullan (Subquery dersinde detaylıca göreceğiz).

IN Performans Notu

-- Az sayıda değer (10-20) → IN gayet performanslı
WHERE city IN ('İstanbul', 'Ankara', 'İzmir');

-- Çok sayıda değer (1000+) → Geçici tablo veya JOIN düşün
-- ❌ 1000 değerli IN listesi performansı düşürebilir
WHERE product_id IN (1, 2, 3, ..., 1000);

-- ✅ Geçici tabloya ekle ve JOIN yap
CREATE TEMPORARY TABLE target_ids (id INT PRIMARY KEY);
INSERT INTO target_ids VALUES (1), (2), (3), ...;
SELECT p.* FROM products p JOIN target_ids t ON p.product_id = t.id;

BETWEEN — Aralık Kontrolü

BETWEEN, bir değerin belirli bir aralıkta olup olmadığını kontrol eder. Alt ve üst sınırlar dahildir.

-- Fiyatı 1000-5000 arası (dahil) olan ürünler
SELECT product_name, price 
FROM products 
WHERE price BETWEEN 1000 AND 5000;

-- Bu şuna eşdeğer:
SELECT product_name, price 
FROM products 
WHERE price >= 1000 AND price <= 5000;

Tarih aralığı:

-- Ocak 2024 siparişleri
SELECT * FROM orders 
WHERE order_date BETWEEN '2024-01-01' AND '2024-01-31';

-- ⚠️ DİKKAT: DATETIME sütununda BETWEEN tarihlerin saat kısmını da kontrol eder!
-- '2024-01-31' aslında '2024-01-31 00:00:00' demektir
-- Yani 31 Ocak'ın öğlen siparişleri bu sorguya dahil olmaz!

-- ✅ Doğru yol:
SELECT * FROM orders 
WHERE order_date BETWEEN '2024-01-01 00:00:00' AND '2024-01-31 23:59:59';

-- ✅ Daha güvenli yol:
SELECT * FROM orders 
WHERE order_date >= '2024-01-01' AND order_date < '2024-02-01';

⚠️ Dikkat: BETWEEN ile tarih sorgularında saat bileşenine dikkat et. DATE tipi sütunlarda sorun yok ama DATETIME ve TIMESTAMP sütunlarında saat bilgisi önemlidir.

NOT BETWEEN:

-- Aşırı ucuz veya aşırı pahalı ürünler (aralık dışı)
SELECT product_name, price 
FROM products 
WHERE price NOT BETWEEN 100 AND 50000;

LIKE — Pattern Matching (Desen Eşleme)

LIKE, metin sütunlarında kalıp eşleme yapar.

Wildcard Karakterler

WildcardAnlamıÖrnekEşleşir
%Sıfır veya daha fazla karakter'A%''A', 'Ahmet', 'Ayşe'
_Tam olarak bir karakter'A____''Ahmet' (A + 4 karakter)

Detaylı Örnekler

-- 'Mac' ile başlayan ürünler
SELECT product_name FROM products WHERE product_name LIKE 'Mac%';
-- MacBook Pro 14"

-- 'Mouse' ile biten ürünler
SELECT product_name FROM products WHERE product_name LIKE '%Mouse';
-- Kablosuz Mouse

-- İçinde 'Pro' geçen ürünler
SELECT product_name FROM products WHERE product_name LIKE '%Pro%';
-- MacBook Pro 14", iPhone 15 Pro

-- 'S' ile başlayan ve 'n' ile biten şehirler
SELECT DISTINCT city FROM customers WHERE city LIKE 'S%n';
-- (varsa eşleşen şehirler)

-- İsmi tam 3 karakter olan müşteriler
SELECT first_name FROM customers WHERE first_name LIKE '___';
-- Ali, Can

-- İkinci harfi 'e' olan isimler
SELECT first_name FROM customers WHERE first_name LIKE '_e%';
-- Zeynep, Mehmet, Selin, Deniz

LIKE ile Özel Karakter Arama (Escape)

Eğer metinde gerçek % veya _ karakterini arıyorsan:

-- İndirim yüzdesi içeren açıklamalar: "50% indirim"
SELECT * FROM products 
WHERE short_description LIKE '%50\%%';
-- \% gerçek % karakterini arar

-- Dosya adında _ olan kayıtlar
SELECT * FROM files WHERE filename LIKE '%\_%';

-- Özel escape karakteri belirle
SELECT * FROM products 
WHERE product_name LIKE '%100!%%' ESCAPE '!';
-- ! karakteri escape olarak kullanılıyor

NOT LIKE

-- E-postası gmail olmayan müşteriler
SELECT first_name, email 
FROM customers 
WHERE email NOT LIKE '%@gmail.com';

LIKE Performans Uyarısı

-- ✅ Hızlı — baştan eşleşme, index kullanabilir
SELECT * FROM products WHERE product_name LIKE 'Mac%';

-- 🐢 Yavaş — ortadan veya sondan eşleşme, index KULLANAMAZ
SELECT * FROM products WHERE product_name LIKE '%Mac%';
SELECT * FROM products WHERE product_name LIKE '%Mac';

💡 İpucu: LIKE 'abc%' (prefix match) index kullanabilir, LIKE '%abc%' kullanamaz. Eğer sıklıkla metin içi arama yapıyorsan, FULLTEXT INDEX kullanmayı düşün (B12'de göreceğiz).


AND, OR, NOT — Mantık Operatörleri Derinlemesine

Operatör Önceliği

SQL'de mantık operatörlerinin değerlendirme sırası:

  1. NOT (en yüksek öncelik)

  2. AND

  3. OR (en düşük öncelik)

-- Bu sorgu nasıl değerlendirilir?
SELECT * FROM products 
WHERE category_id = 1 OR category_id = 2 AND price > 10000;

-- MySQL bunu şöyle okur:
-- category_id = 1 OR (category_id = 2 AND price > 10000)
-- Yani: TÜM Elektronik + 10000 üstü Bilgisayarlar

-- İstediğimiz muhtemelen bu:
SELECT * FROM products 
WHERE (category_id = 1 OR category_id = 2) AND price > 10000;
-- Elektronik veya Bilgisayar KATEGORİSİNDE VE 10000 üstü

Karmaşık Koşullar — Parantez Kullanımı

-- Karmaşık filtre: 
-- İstanbul veya Ankara'daki aktif müşteriler
-- VEYA herhangi bir şehirde ama gmail kullananlar
SELECT * FROM customers 
WHERE (city IN ('İstanbul', 'Ankara') AND is_active = TRUE)
   OR (email LIKE '%@gmail.com');

-- E-ticaret ürün filtresi:
-- Elektronik (1) kategorisinde fiyatı 500+ olan
-- VEYA Kitap (7) kategorisinde tüm ürünler
-- VE stokta olmalı
SELECT * FROM products 
WHERE ((category_id = 1 AND price >= 500) OR category_id = 7)
  AND stock_quantity > 0
  AND is_active = TRUE;

NOT ile Karmaşık İfadeler

-- NOT ile IN
SELECT * FROM customers WHERE city NOT IN ('İstanbul', 'Ankara');

-- NOT ile BETWEEN
SELECT * FROM products WHERE price NOT BETWEEN 100 AND 1000;

-- NOT ile LIKE
SELECT * FROM customers WHERE email NOT LIKE '%@gmail.com';

-- NOT ile AND/OR — dikkatli ol!
SELECT * FROM products WHERE NOT (category_id = 1 AND price > 10000);
-- Bu = category_id != 1 OR price <= 10000 (De Morgan kuralı)

REGEXP / RLIKE — Düzenli İfadeler (Regular Expressions)

LIKE sınırlı kalıyorsa, REGEXP daha güçlü pattern matching sağlar:

-- İsmi 'A' veya 'E' ile başlayan müşteriler
SELECT first_name FROM customers WHERE first_name REGEXP '^[AE]';
-- Ahmet, Ayşe, Elif

-- İsmi 'n' veya 't' ile biten müşteriler
SELECT first_name FROM customers WHERE first_name REGEXP '[nt]$';
-- Ahmet, Mehmet, Can

-- E-posta formatı kontrolü (basitleştirilmiş)
SELECT email FROM customers WHERE email REGEXP '^[a-zA-Z0-9.]+@[a-zA-Z]+\\.[a-zA-Z]{2,}$';

-- Ürün adında sayı geçen ürünler
SELECT product_name FROM products WHERE product_name REGEXP '[0-9]';
-- MacBook Pro 14", iPhone 15 Pro, Samsung Galaxy S24, Dell XPS 15, Monitör 27"

REGEXP pattern'ları:

PatternAnlamıÖrnek
^Başlangıç'^A' → A ile başlar
$Son't$' → t ile biter
.Herhangi bir karakter'A.B' → AXB, A1B...
[abc]Karakter seti'[AEI]' → A, E veya I
[a-z]Karakter aralığı'[a-z]' → küçük harf
*0 veya daha fazla tekrar'ab*' → a, ab, abb...
+1 veya daha fazla tekrar'ab+' → ab, abb...
{n}Tam n tekrar'[0-9]{3}' → 3 haneli sayı
`\`VEYA`'cat\dog'` → cat veya dog

📝 PostgreSQL Notu: PostgreSQL'de REGEXP yerine ~ (case-sensitive) ve ~* (case-insensitive) operatörleri kullanılır: ``sql SELECT * FROM customers WHERE first_name ~ '^[AE]'; ``

💡 İpucu: REGEXP, LIKE'tan çok daha güçlü ama daha yavaştır (index kullanamaz). Basit aramalar için LIKE yeterli — REGEXP'i karmaşık pattern'lar için sakla.


Gerçek Dünya Örneği — E-Ticaret Arama ve Filtreleme

-- Senaryo: Kullanıcı ürün sayfasında filtre uyguluyor
-- Filtreler: Kategori = Elektronik veya Bilgisayar
--            Fiyat: 500 - 60000 TL
--            Stokta var
--            Arama: "pro" kelimesi

SELECT 
    p.product_id,
    p.product_name,
    p.price,
    p.stock_quantity,
    c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id
WHERE p.category_id IN (1, 2, 3)             -- Elektronik + alt kategoriler
  AND p.price BETWEEN 500 AND 60000           -- Fiyat aralığı
  AND p.stock_quantity > 0                     -- Stokta var
  AND p.is_active = TRUE                       -- Aktif ürünler
  AND p.product_name LIKE '%Pro%'              -- Arama terimi
ORDER BY p.price ASC;                          -- Fiyata göre sırala

Sıkça Yapılan Hatalar

  1. AND/OR önceliğini karıştırmak: A OR B AND CA OR (B AND C). Her zaman parantez kullan.

  2. NULL ile karşılaştırma yapmak: WHERE city = NULL çalışmaz. WHERE city IS NULL kullan. Bu çok önemli bir konu, bir sonraki derste detaylıca işleyeceğiz.

  3. LIKE'ta escape unutmak: Veri içinde % veya _ geçiyorsa ve bu karakterleri aramak istiyorsan \% veya \_ kullan.

  4. BETWEEN'in sınırları dahil ettiğini unutmak: BETWEEN 1 AND 10, 1 ve 10'u da dahil eder. Hariç tutmak istiyorsan > 1 AND < 10 kullan.

  5. NOT IN ile NULL sorunu: NOT IN (1, 2, NULL) hiçbir zaman TRUE döndürmez. Subquery'den NULL gelebilecekse NOT EXISTS tercih et.

  6. Case sensitivity karışıklığı: Collation'a bağlı. _ci collation'da 'AHMET' = 'ahmet'. Case-sensitive gerekiyorsa BINARY veya _bin collation kullan.


Özet

  • Karşılaştırma operatörleri: =, !=/<>, >, <, >=, <=

  • IN birden fazla değer kontrolü için kullanılır, OR zincirinin kısaltmasıdır

  • BETWEEN aralık kontrolü yapar, alt ve üst sınırlar dahildir

  • LIKE metin arama: % (sıfır veya çok karakter), _ (tek karakter)

  • REGEXP daha güçlü pattern matching ama daha yavaş

  • Mantık operatörleri önceliği: NOT > AND > ORparantez kullan

  • NOT IN ile NULL'a dikkat et

  • LIKE 'abc%' index kullanabilir, LIKE '%abc%' kullanamaz

  • Karmaşık koşulları her zaman parantezle grupla