MariaDB ve MySQL’de AND ve OR Operatörleri ile Çoklu Koşul Kullanımı

Veritabanı sorgularında tek bir koşula bağlı kalmak çoğu zaman yetersiz kalır. Gerçek dünya senaryolarında “şu tarihten sonra kaydedilmiş VE aktif olan kullanıcıları getir” ya da “İstanbul’daki VEYA Ankara’daki müşterileri listele” gibi kombinasyonlara ihtiyaç duyarsınız. İşte tam bu noktada AND ve OR operatörleri devreye girer. Bu yazıda MariaDB ve MySQL üzerinde AND/OR operatörlerini nasıl etkili kullanacağınızı, öncelik sıralamasını ve gerçek dünya örneklerini ele alacağız.

AND ve OR Operatörlerine Genel Bakış

AND operatörü, belirtilen tüm koşulların aynı anda sağlanmasını gerektirir. Yani bir satırın sonuç kümesine dahil edilebilmesi için yazılan her koşulun doğru olması gerekir. OR operatörü ise belirtilen koşullardan en az birinin sağlanmasını yeterli görür.

Bu iki operatör arasındaki farkı somutlaştıralım:

  • AND: Tüm koşullar doğru olmalı (kesişim kümesi)
  • OR: En az bir koşul doğru olmalı (birleşim kümesi)
  • Öncelik sırası: AND, OR’dan daha yüksek önceliğe sahiptir
  • Parantez kullanımı: Öncelik sırasını değiştirmek için parantez şarttır

Örnek Veritabanı Yapısı

Yazı boyunca kullanacağımız örnek tabloları oluşturalım. Gerçekçi bir e-ticaret senaryosu üzerine kurulu bu yapı, farklı sorgu kombinasyonlarını test etmemizi kolaylaştıracak.

CREATE DATABASE IF NOT EXISTS eticaret;
USE eticaret;

CREATE TABLE musteriler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ad VARCHAR(100) NOT NULL,
    soyad VARCHAR(100) NOT NULL,
    sehir VARCHAR(100),
    yas INT,
    durum ENUM('aktif', 'pasif', 'beklemede') DEFAULT 'aktif',
    kayit_tarihi DATE,
    toplam_harcama DECIMAL(10,2) DEFAULT 0.00
);

CREATE TABLE siparisler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    musteri_id INT,
    urun_adi VARCHAR(200),
    kategori VARCHAR(100),
    miktar INT,
    birim_fiyat DECIMAL(10,2),
    siparis_tarihi DATE,
    kargo_durumu ENUM('bekliyor', 'kargoda', 'teslim_edildi', 'iptal') DEFAULT 'bekliyor',
    FOREIGN KEY (musteri_id) REFERENCES musteriler(id)
);

INSERT INTO musteriler (ad, soyad, sehir, yas, durum, kayit_tarihi, toplam_harcama) VALUES
('Ahmet', 'Yılmaz', 'İstanbul', 34, 'aktif', '2022-01-15', 4500.00),
('Mehmet', 'Kaya', 'Ankara', 28, 'aktif', '2021-06-20', 12000.00),
('Ayşe', 'Demir', 'İzmir', 45, 'pasif', '2020-03-10', 800.00),
('Fatma', 'Çelik', 'İstanbul', 22, 'aktif', '2023-02-28', 2300.00),
('Ali', 'Şahin', 'Bursa', 55, 'beklemede', '2019-11-05', 6700.00),
('Zeynep', 'Arslan', 'Ankara', 31, 'aktif', '2022-08-14', 9800.00),
('Hasan', 'Koç', 'İstanbul', 19, 'pasif', '2023-05-01', 150.00),
('Elif', 'Yıldız', 'İzmir', 38, 'aktif', '2021-12-22', 15400.00);

Temel AND Kullanımı

AND operatörünün en basit kullanımı iki koşulu birleştirmektir. Aktif olan ve İstanbul’da yaşayan müşterileri çekelim:

SELECT ad, soyad, sehir, durum, toplam_harcama
FROM musteriler
WHERE durum = 'aktif'
  AND sehir = 'İstanbul';

Bu sorgu yalnızca her iki koşulu da sağlayan kayıtları döndürür. Bir müşteri aktif olsa bile İstanbul’da değilse veya İstanbul’da olsa bile aktif değilse sonuç kümesine girmez.

Birden fazla AND kullanarak koşulları zincirleme şeklinde yazabilirsiniz:

SELECT ad, soyad, sehir, yas, toplam_harcama
FROM musteriler
WHERE durum = 'aktif'
  AND sehir = 'İstanbul'
  AND yas >= 25
  AND toplam_harcama > 2000.00;

Bu sorgu; aktif olan, İstanbul’da yaşayan, 25 yaş ve üzeri olan ve 2000 TL’nin üzerinde harcama yapmış müşterileri getirir. Her koşul eklendikçe sonuç kümesi küçülür.

Temel OR Kullanımı

OR operatörü ile birden fazla şehirdeki müşterileri tek sorguda çekebilirsiniz:

SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE sehir = 'İstanbul'
   OR sehir = 'Ankara'
   OR sehir = 'İzmir';

Bu sorgunun sonucu belirtilen üç şehirden herhangi birinde yaşayan tüm müşterileri kapsar. Aynı sonucu IN operatörüyle daha temiz bir şekilde yazabilirsiniz, ancak OR operatörünü anlamak temel mantığı kavramak açısından önemlidir.

OR ile farklı alanlardaki koşulları da birleştirebilirsiniz:

SELECT ad, soyad, durum, toplam_harcama
FROM musteriler
WHERE toplam_harcama > 10000.00
   OR durum = 'beklemede';

Bu sorgu; ya 10.000 TL’nin üzerinde harcama yapmış ya da hesabı beklemede statüsünde olan müşterileri getirir. Bir müşteri bu iki koşuldan birini sağladığı sürece sonuçta görünür.

AND ve OR Birlikte Kullanımı ve Öncelik Sorunu

İşte tam bu noktada dikkatli olmanız gereken kritik bir konu var. AND ve OR birlikte kullanıldığında AND önce değerlendirilir. Bu durum beklenmedik sonuçlara yol açabilir.

Şöyle bir senaryo düşünelim: “İstanbul veya Ankara’da yaşayan aktif müşterileri” bulmak istiyorsunuz.

-- YANLIS: Bu sorgu istediginizi yapmaz
SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE durum = 'aktif'
  AND sehir = 'İstanbul'
   OR sehir = 'Ankara';

Bu sorgu aslında şu şekilde değerlendirilir: “(durum = ‘aktif’ AND sehir = ‘İstanbul’) OR sehir = ‘Ankara'”. Yani Ankara’daki TÜM müşterileri getirir, aktif olup olmadıklarına bakmaksızın.

Doğru yazım parantez kullanımı gerektirir:

-- DOGRU: Parantez ile oncelik belirlendi
SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE durum = 'aktif'
  AND (sehir = 'İstanbul' OR sehir = 'Ankara');

Bu sürüm tam olarak istediğinizi yapar: Aktif olan VE (İstanbul’da VEYA Ankara’da yaşayan) müşterileri getirir.

Gerçek Dünya Senaryosu: Müşteri Segmentasyonu

Bir pazarlama kampanyası için müşteri segmentasyonu yapmanız gerektiğini düşünün. Hedef kitleniz: 30 yaş altında ve yüksek harcamalı müşteriler VEYA 30 yaş üstünde ve orta segment müşteriler.

SELECT 
    ad,
    soyad,
    yas,
    sehir,
    toplam_harcama,
    durum,
    CASE 
        WHEN yas < 30 AND toplam_harcama > 5000 THEN 'Genç Premium'
        WHEN yas >= 30 AND toplam_harcama BETWEEN 2000 AND 10000 THEN 'Orta Segment'
        ELSE 'Diger'
    END AS musteri_segmenti
FROM musteriler
WHERE durum = 'aktif'
  AND (
    (yas < 30 AND toplam_harcama > 5000)
    OR
    (yas >= 30 AND toplam_harcama BETWEEN 2000 AND 10000)
  );

Bu sorgu, iç içe geçmiş AND/OR kombinasyonu ile karmaşık bir segmentasyon mantığını başarıyla uygular. Parantezlerin yerleşimi tamamen kasıtlı: Dış AND aktif olma koşulunu zorunlu kılarken, iç parentezler iki farklı segment grubundan birini sağlayan kayıtları birleştirir.

Gerçek Dünya Senaryosu: Sipariş Takip Sistemi

E-ticaret operasyonlarında sık karşılaşılan bir senaryo: Sorunlu siparişleri tespit etmek. “Son 30 gün içinde verilen ve ya kargo durumu hala ‘bekliyor’ olan siparişleri VEYA iptal edilmiş ama müşterisi aktif olan siparişleri” bulmak istiyorsunuz:

SELECT 
    s.id AS siparis_id,
    m.ad,
    m.soyad,
    s.urun_adi,
    s.kargo_durumu,
    s.siparis_tarihi,
    m.durum AS musteri_durumu
FROM siparisler s
JOIN musteriler m ON s.musteri_id = m.id
WHERE 
    (
        s.siparis_tarihi >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
        AND s.kargo_durumu = 'bekliyor'
    )
    OR
    (
        s.kargo_durumu = 'iptal'
        AND m.durum = 'aktif'
    )
ORDER BY s.siparis_tarihi DESC;

Bu sorgu, operasyon ekibinin her sabah kontrol etmesi gereken bir dashboard için mükemmel bir temel oluşturur. İki farklı sorun grubunu tek sorguda yakalar: geciken siparişler ve aktif müşterilere ait iptal siparişleri.

NULL Değerleri ile AND ve OR

AND ve OR operatörlerinin NULL değerlerle nasıl davrandığını bilmek kritik önem taşır. MySQL’in üç değerli mantık sistemi (TRUE, FALSE, NULL/UNKNOWN) bu konuda sürprizler yaratabilir.

-- NULL kontrolu ile guvenli sorgu
SELECT ad, soyad, sehir, yas
FROM musteriler
WHERE (sehir IS NOT NULL AND sehir = 'İstanbul')
   OR (yas IS NOT NULL AND yas < 25);

Daha pratik bir örnek: Bazı müşteri kayıtlarında sehir bilgisi eksik olabilir. Bu müşterileri de belirli bir koşulla birlikte getirmek isteyebilirsiniz:

SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE durum = 'aktif'
  AND (sehir = 'İstanbul' OR sehir IS NULL);

Bu sorgu, aktif olan ve ya İstanbul’da yaşayan ya da şehri bilinmeyen müşterileri getirir. İkinci grup, veri temizleme süreci için ayrıca işlenebilir.

Performans Açısından AND ve OR

Sysadmin perspektifinden bakıldığında, AND ve OR kullanımının performans üzerindeki etkisi göz ardı edilemez.

AND operatörü ve optimizasyon: MySQL/MariaDB query optimizer, AND koşullarını değerlendirirken kısa devre mantığı uygular. İlk koşul FALSE döndürürdüğünde diğer koşullar değerlendirilmez. Bu nedenle en seçici (en az kayıt döndüren) koşulu AND zincirinin başına koymak performansı artırır.

OR operatörü ve index kullanımı: OR operatörü, optimizer için genellikle daha zorlu bir durumdur. OR kullanılan sütunlar aynı tabloda ayrı ayrı indekslenmiş olsa bile optimizer her zaman bu indeksleri kullanmayabilir.

EXPLAIN ile sorgularınızın performansını kontrol edin:

EXPLAIN SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE (sehir = 'İstanbul' OR sehir = 'Ankara')
  AND durum = 'aktif';

EXPLAIN çıktısında “type” sütununu inceleyin:

  • const: Tek satır döndürür, en iyi
  • ref: İndeks kullanılıyor
  • range: İndeks aralığı taraması
  • ALL: Full table scan, dikkat

OR koşullarında genellikle UNION kullanımı daha iyi performans sağlar:

-- OR yerine UNION ile daha iyi performans
SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE sehir = 'İstanbul' AND durum = 'aktif'
UNION
SELECT ad, soyad, sehir, durum
FROM musteriler
WHERE sehir = 'Ankara' AND durum = 'aktif';

Her iki sorgu da ayrı ayrı indeks kullanabilir. Büyük tablolarda bu yaklaşım ciddi performans kazanımı sağlar.

Karmaşık İş Kurallarını Modelleme

Gerçek projelerde karmaşık iş kurallarını SQL’e dönüştürmeniz gerekir. Örneğin bir insan kaynakları sisteminde “prim almaya hak kazanan çalışanları” bulmak için şu kural seti tanımlanmış olsun:

Kural: (Satış hedefini tutturmuş VE en az 1 yıldır çalışıyor) VEYA (Yönetici onayı var VE hiç devamsızlığı yok) VEYA (Şirket yılı 5’ten fazla VE performans puanı 85’ten yüksek)

-- Ornek calisan tablosu ile prim sorgusu
SELECT 
    c.ad,
    c.soyad,
    c.departman,
    c.calisma_yili,
    c.satis_hedef_yuzdesi,
    c.performans_puani,
    c.yonetici_onayi,
    c.devamsizlik_sayisi,
    'Prim Hakki Var' AS durum
FROM calisanlar c
WHERE 
    (
        c.satis_hedef_yuzdesi >= 100 
        AND c.calisma_yili >= 1
    )
    OR 
    (
        c.yonetici_onayi = 1 
        AND c.devamsizlik_sayisi = 0
    )
    OR 
    (
        c.calisma_yili >= 5 
        AND c.performans_puani > 85
    );

Bu tür karmaşık iş kurallarını SQL’e aktarırken her koşul grubunu parantez içine almak hem okunabilirliği hem de doğruluğu garanti eder. Kodu okuyan başka bir sysadmin veya developer, iş kurallarını SQL’den direkt okuyabilmelidir.

WHERE ile AND/OR Kombinasyonu ve Agregat Fonksiyonlar

AND ve OR yalnızca WHERE cümleciğiyle sınırlı değildir. HAVING ile birlikte agregat sorguları da yazabilirsiniz:

SELECT 
    sehir,
    COUNT(*) AS musteri_sayisi,
    AVG(toplam_harcama) AS ortalama_harcama,
    MAX(toplam_harcama) AS max_harcama
FROM musteriler
WHERE durum = 'aktif'
  AND kayit_tarihi >= '2021-01-01'
GROUP BY sehir
HAVING COUNT(*) >= 2
   AND AVG(toplam_harcama) > 3000.00
ORDER BY ortalama_harcama DESC;

Bu sorgu; aktif olan ve 2021’den sonra kayıt olmuş müşteriler arasından, en az 2 müşterisi bulunan ve ortalama harcaması 3000 TL’nin üzerinde olan şehirleri listeler. WHERE ve HAVING cümleciklerinde AND’in farklı seviyelerde çalıştığına dikkat edin.

Stored Procedure ile Dinamik Koşul Yönetimi

Büyük projelerde AND/OR koşulları bir stored procedure içinde dinamik olarak yönetilebilir. Bu yaklaşım, uygulama katmanından çok fazla koşul gönderildiği durumlarda kodu temiz tutar:

DELIMITER //

CREATE PROCEDURE musteri_filtrele(
    IN p_sehir VARCHAR(100),
    IN p_durum VARCHAR(50),
    IN p_min_harcama DECIMAL(10,2),
    IN p_max_yas INT
)
BEGIN
    SELECT 
        ad,
        soyad,
        sehir,
        yas,
        durum,
        toplam_harcama
    FROM musteriler
    WHERE 
        (p_sehir IS NULL OR sehir = p_sehir)
        AND (p_durum IS NULL OR durum = p_durum)
        AND (p_min_harcama IS NULL OR toplam_harcama >= p_min_harcama)
        AND (p_max_yas IS NULL OR yas <= p_max_yas);
END //

DELIMITER ;

-- Kullanim ornekleri
CALL musteri_filtrele('İstanbul', 'aktif', NULL, NULL);
CALL musteri_filtrele(NULL, 'aktif', 5000.00, 40);
CALL musteri_filtrele('Ankara', NULL, 3000.00, 35);

Bu teknik “optional filter” pattern olarak bilinir. NULL gönderilen parametreler koşuldan hariç tutulur çünkü (NULL IS NULL OR ...) ifadesi her zaman TRUE döner. Böylece tek bir stored procedure ile onlarca farklı filtreleme kombinasyonu desteklenebilir.

Sık Yapılan Hatalar ve Çözümleri

  • Parantez unutmak: AND ve OR’u karıştırdığınızda mutlaka parantez kullanın. “Benim aklımda net” demek yetmez, başkasının okuması gerekebilir.
  • NOT operatörüyle yanlış kombinasyon: NOT A AND B ile NOT (A AND B) farklı sonuçlar üretir. NOT’un kapsamını parantezle netleştirin.
  • OR ile indeks kaybı: Farklı sütunlarda OR kullanıyorsanız EXPLAIN çıktısını mutlaka kontrol edin, gerekirse UNION’a geçin.
  • BETWEEN ile AND karışıklığı: BETWEEN 10 AND 20 ifadesindeki AND, mantıksal AND operatörü değildir. Bu BETWEEN’e özel bir sözdizimdir.
  • Okunabilirlik ihmal: Uzun AND/OR zincirlerini alt alta yazın ve her koşulu aynı hizaya getirin. Sıkıştırılmış sorgu yazmak hata yapmayı kolaylaştırır.

Sonuç

AND ve OR operatörleri SQL’in temel yapı taşlarından olmalarına karşın, yanlış kullanıldığında ciddi veri tutarsızlıklarına ve performans sorunlarına neden olabilir. Bu yazıda ele aldığımız konuları özetlersek:

  • AND tüm koşulların doğru olmasını isterken, OR en az birinin doğru olmasını yeterli bulur.
  • AND, OR’dan daha yüksek önceliğe sahiptir ve bu durum parantez kullanımını zorunlu kılar.
  • Karmaşık iş kurallarını modellerken her koşul grubunu parantez içine almak hem güvenlik hem de okunabilirlik sağlar.
  • OR operatörü büyük tablolarda performans sorununa yol açabilir; EXPLAIN ile kontrol edin ve gerekirse UNION tercih edin.
  • NULL değerlerin AND/OR mantığı üzerindeki etkisini anlayın ve IS NULL/IS NOT NULL kontrollerini ihmal etmeyin.
  • Stored procedure’larda opsiyonel filtre pattern’ı ile dinamik ve temiz sorgu yönetimi sağlayabilirsiniz.

Günlük veritabanı yönetimi işlerinde bu operatörlere hakim olmak, hem yazacağınız sorguların doğruluğunu hem de sistemin genel performansını doğrudan etkiler. Bir sonraki yazıda NOT operatörü ve daha ileri düzey koşul kombinasyonlarını ele alacağız.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir