MariaDB ve MySQL’de WHERE Koşulu ile Veri Filtreleme
Veritabanı yönetiminde en sık kullandığımız ve aslında en çok hata yaptığımız yerlerden biri WHERE koşullarıdır. Yanlış yazılmış bir WHERE koşulu, milyonlarca satırlık tablodan yanlış veri çekmekle kalmaz, bazen tüm tabloyu silmenize de yol açabilir. Bu yazıda WHERE koşulunu her yönüyle ele alacağız; temel kullanımdan karmaşık senaryolara kadar gerçek dünya örnekleriyle ilerleyeceğiz.
WHERE Koşulu Neden Bu Kadar Önemli?
Bir üretim ortamında çalışırken sık sık şunu görürüm: Junior DBA’ler ya da geliştiriciler WHERE koşulu olmadan UPDATE veya DELETE çalıştırır. Sonuç felaket olur. MariaDB ve MySQL’de WHERE koşulu, sorgunuzun hangi satırlar üzerinde işlem yapacağını belirleyen temel filtre mekanizmasıdır.
SELECT, UPDATE, DELETE ve hatta JOIN sorgularında WHERE koşulu kullanabilirsiniz. Doğru kullanıldığında hem performans kazanırsınız hem de veri bütünlüğünü korursunuz.
Önce çalışacağımız örnek veritabanını ve tabloları oluşturalım:
-- Örnek veritabanı ve tablolar
CREATE DATABASE sirket_db;
USE sirket_db;
CREATE TABLE calisanlar (
id INT AUTO_INCREMENT PRIMARY KEY,
ad VARCHAR(50),
soyad VARCHAR(50),
departman VARCHAR(50),
maas DECIMAL(10,2),
ise_giris_tarihi DATE,
aktif TINYINT(1) DEFAULT 1,
sehir VARCHAR(50)
);
CREATE TABLE siparisler (
siparis_id INT AUTO_INCREMENT PRIMARY KEY,
musteri_id INT,
urun_adi VARCHAR(100),
miktar INT,
fiyat DECIMAL(10,2),
siparis_tarihi DATETIME,
durum VARCHAR(20)
);
-- Örnek veri ekleyelim
INSERT INTO calisanlar (ad, soyad, departman, maas, ise_giris_tarihi, aktif, sehir) VALUES
('Ahmet', 'Yilmaz', 'IT', 8500.00, '2019-03-15', 1, 'Istanbul'),
('Fatma', 'Kaya', 'Muhasebe', 6200.00, '2020-07-01', 1, 'Ankara'),
('Mehmet', 'Demir', 'IT', 9200.00, '2018-11-20', 1, 'Istanbul'),
('Ayse', 'Celik', 'Pazarlama', 7100.00, '2021-02-10', 0, 'Izmir'),
('Ali', 'Sahin', 'IT', 11000.00, '2017-06-05', 1, 'Istanbul'),
('Zeynep', 'Arslan', 'Muhasebe', 6800.00, '2022-01-15', 1, 'Ankara'),
('Mustafa', 'Ozturk', 'Pazarlama', 7500.00, '2019-09-30', 1, 'Izmir'),
('Elif', 'Yildiz', 'IT', 8900.00, '2020-04-22', 1, 'Istanbul');
Temel WHERE Kullanımı
WHERE koşulunun en basit hali eşitlik kontrolüdür. Karşılaştırma operatörleri ile başlayalım:
-- Eşitlik kontrolü
SELECT * FROM calisanlar WHERE departman = 'IT';
-- Eşit değil kontrolü
SELECT * FROM calisanlar WHERE departman != 'IT';
-- veya
SELECT * FROM calisanlar WHERE departman <> 'IT';
-- Büyük/küçük karşılaştırmalar
SELECT ad, soyad, maas FROM calisanlar WHERE maas > 8000;
SELECT ad, soyad, maas FROM calisanlar WHERE maas >= 8500;
SELECT ad, soyad, maas FROM calisanlar WHERE maas < 7000;
SELECT ad, soyad, maas FROM calisanlar WHERE maas <= 6800;
Burada dikkat etmeniz gereken bir nokta var: MySQL ve MariaDB’de string karşılaştırmaları varsayılan olarak büyük/küçük harf duyarsızdır (case-insensitive). Yani WHERE departman = 'it' ile WHERE departman = 'IT' aynı sonucu verir. Eğer büyük/küçük harf duyarlı karşılaştırma yapmanız gerekiyorsa BINARY anahtar kelimesini kullanın:
-- Case-sensitive karşılaştırma
SELECT * FROM calisanlar WHERE BINARY departman = 'it';
-- Bu sorgu sonuç döndürmez çünkü 'IT' != 'it' (BINARY ile)
SELECT * FROM calisanlar WHERE BINARY departman = 'IT';
-- Bu çalışır
AND, OR ve NOT ile Koşul Kombinasyonları
Gerçek dünyada nadiren tek koşulla filtreleme yaparsınız. Birden fazla koşulu birleştirmeniz gerekir.
-- AND operatörü: Her iki koşul da doğru olmalı
SELECT ad, soyad, departman, maas
FROM calisanlar
WHERE departman = 'IT' AND maas > 9000;
-- OR operatörü: En az bir koşul doğru olmalı
SELECT ad, soyad, sehir
FROM calisanlar
WHERE sehir = 'Istanbul' OR sehir = 'Ankara';
-- NOT operatörü: Koşulun tersi
SELECT ad, soyad, aktif
FROM calisanlar
WHERE NOT aktif = 1;
-- veya daha okunabilir hali:
SELECT ad, soyad, aktif
FROM calisanlar
WHERE aktif != 1;
-- Karmaşık kombinasyon: AND ve OR birlikte
SELECT ad, soyad, departman, maas, sehir
FROM calisanlar
WHERE (departman = 'IT' OR departman = 'Muhasebe')
AND maas > 7000
AND aktif = 1;
Önemli uyarı: AND ve OR’u birlikte kullandığınızda operatör önceliğine dikkat edin. AND, OR’dan daha yüksek önceliğe sahiptir. Beklediğiniz sonucu almak için parantez kullanmayı alışkanlık edinin.
BETWEEN ile Aralık Filtreleme
Bir değer aralığını filtrelemek için BETWEEN operatörünü kullanabilirsiniz. Hem sayısal değerler hem de tarihler için çalışır.
-- Maaş aralığı filtreleme
SELECT ad, soyad, maas
FROM calisanlar
WHERE maas BETWEEN 7000 AND 9000;
-- BETWEEN başlangıç ve bitiş değerlerini dahil eder (inclusive)
-- Tarih aralığı filtreleme
SELECT ad, soyad, ise_giris_tarihi
FROM calisanlar
WHERE ise_giris_tarihi BETWEEN '2019-01-01' AND '2021-12-31';
-- NOT BETWEEN ile aralık dışını filtreleme
SELECT ad, soyad, maas
FROM calisanlar
WHERE maas NOT BETWEEN 6000 AND 8000;
-- Sipariş tablosunda tarih ve saat aralığı
SELECT siparis_id, urun_adi, siparis_tarihi
FROM siparisler
WHERE siparis_tarihi BETWEEN '2024-01-01 00:00:00' AND '2024-01-31 23:59:59';
BETWEEN kullanırken dikkat edin: başlangıç değeri bitiş değerinden küçük olmak zorundadır. WHERE maas BETWEEN 9000 AND 7000 yazarsanız sonuç döndürmez.
IN ve NOT IN ile Liste Filtreleme
Birden fazla OR koşulu yazmak yerine IN operatörünü kullanmak hem daha temiz kod yazar hem de bazı durumlarda daha iyi performans sağlar.
-- Birden fazla şehir için filtreleme
SELECT ad, soyad, sehir, departman
FROM calisanlar
WHERE sehir IN ('Istanbul', 'Ankara');
-- Bu sorgu şununla eşdeğerdir ama daha temizdir:
-- WHERE sehir = 'Istanbul' OR sehir = 'Ankara'
-- NOT IN: Listede olmayanları getir
SELECT ad, soyad, departman
FROM calisanlar
WHERE departman NOT IN ('IT', 'Muhasebe');
-- Alt sorgu ile IN kullanımı (subquery)
-- Maaşı ortalamanın üzerinde olan çalışanların departmanları
SELECT DISTINCT departman
FROM calisanlar
WHERE id IN (
SELECT id FROM calisanlar WHERE maas > (SELECT AVG(maas) FROM calisanlar)
);
Performans notu: IN içindeki liste çok uzunsa (yüzlerce, binlerce eleman) performans sorunuyla karşılaşabilirsiniz. Bu durumda geçici tablo veya JOIN kullanmayı değerlendirin.
LIKE ile Metin Arama
Tam eşleşme yerine kısmi metin araması yapmanız gerektiğinde LIKE operatörü devreye girer. İki joker karakter vardır:
- %: Sıfır veya daha fazla herhangi bir karakteri temsil eder
- _: Tam olarak bir karakteri temsil eder
-- Adı 'A' ile başlayanlar
SELECT ad, soyad FROM calisanlar WHERE ad LIKE 'A%';
-- Soyadı 'iz' ile bitenler
SELECT ad, soyad FROM calisanlar WHERE soyad LIKE '%iz';
-- İçinde 'li' geçenler
SELECT ad, soyad FROM calisanlar WHERE ad LIKE '%li%';
-- Tam olarak 5 karakterli adlar
SELECT ad, soyad FROM calisanlar WHERE ad LIKE '_____';
-- Adı 'A' ile başlayan ve tam 4 karakterli olanlar
SELECT ad, soyad FROM calisanlar WHERE ad LIKE 'A___';
-- NOT LIKE kullanımı
SELECT ad, soyad, departman
FROM calisanlar
WHERE departman NOT LIKE 'IT%';
-- LIKE ile büyük/küçük harf duyarlı arama
-- Varsayılan charset'te LIKE zaten case-insensitive
-- Case-sensitive için:
SELECT ad, soyad FROM calisanlar WHERE ad LIKE BINARY 'ahmet%';
Dikkat: LIKE ‘%metin%’ şeklindeki aramalar index kullanamaz ve büyük tablolarda ciddi performans sorununa yol açar. Büyük tablolarda metin araması için FULLTEXT index ve MATCH…AGAINST kullanmayı düşünün.
NULL Değer Kontrolü
NULL değeri olan kayıtları filtrelemek için özel bir sözdizimi kullanmanız gerekir. WHERE alan = NULL yazmak çalışmaz, çünkü NULL hiçbir değerle eşit değildir.
-- NULL değerleri bulmak için IS NULL kullanın
SELECT ad, soyad FROM calisanlar WHERE sehir IS NULL;
-- NULL olmayan değerleri bulmak için IS NOT NULL
SELECT ad, soyad FROM calisanlar WHERE sehir IS NOT NULL;
-- NULL değerleri olan kayıtları güncellemek
UPDATE calisanlar SET sehir = 'Bilinmiyor' WHERE sehir IS NULL;
-- IFNULL ile NULL değerini varsayılan değerle değiştirme
SELECT ad, soyad, IFNULL(sehir, 'Belirtilmemiş') AS sehir
FROM calisanlar;
-- COALESCE ile birden fazla alan için NULL kontrolü
SELECT ad, soyad, COALESCE(sehir, departman, 'Veri Yok') AS konum
FROM calisanlar;
Gerçek Dünya Senaryoları
Teorik bilgiyi bir kenara bırakıp gerçek hayatta karşılaştığım senaryolara bakalım.
Senaryo 1: Aktif Olmayan Kullanıcı Temizliği
Bir kurumsal uygulamada 2 yılı aşkın süredir giriş yapmamış ve aktif olmayan kullanıcıları tespit etmek gerekiyor:
-- Önce kaç kayıt etkileneceğini kontrol edin (ASLA direkt DELETE atmayın)
SELECT COUNT(*) as etkilenecek_kayit_sayisi
FROM calisanlar
WHERE aktif = 0
AND ise_giris_tarihi < DATE_SUB(NOW(), INTERVAL 2 YEAR);
-- Detaylarını görün
SELECT id, ad, soyad, ise_giris_tarihi, aktif
FROM calisanlar
WHERE aktif = 0
AND ise_giris_tarihi < DATE_SUB(NOW(), INTERVAL 2 YEAR)
ORDER BY ise_giris_tarihi ASC;
-- Onayladıktan sonra işlem yapın
-- Önce arşiv tablosuna kopyalayın
INSERT INTO calisanlar_arsiv
SELECT * FROM calisanlar
WHERE aktif = 0
AND ise_giris_tarihi < DATE_SUB(NOW(), INTERVAL 2 YEAR);
-- Sonra sil
DELETE FROM calisanlar
WHERE aktif = 0
AND ise_giris_tarihi < DATE_SUB(NOW(), INTERVAL 2 YEAR);
Senaryo 2: Maaş Güncelleme
IT departmanında çalışan ve 3 yılı aşkın kıdeme sahip, aktif çalışanlara %10 zam yapılacak:
-- Önce etkilenecek kayıtları görüntüle
SELECT id, ad, soyad, maas, ise_giris_tarihi,
maas * 1.10 AS yeni_maas
FROM calisanlar
WHERE departman = 'IT'
AND aktif = 1
AND ise_giris_tarihi <= DATE_SUB(NOW(), INTERVAL 3 YEAR);
-- Sonuçları onayladıktan sonra güncelle
UPDATE calisanlar
SET maas = maas * 1.10
WHERE departman = 'IT'
AND aktif = 1
AND ise_giris_tarihi <= DATE_SUB(NOW(), INTERVAL 3 YEAR);
-- Güncellemeyi doğrula
SELECT ad, soyad, maas FROM calisanlar
WHERE departman = 'IT'
AND aktif = 1;
Senaryo 3: Performans Sorunu Tespiti
Bir müşteri şikayeti aldığınızda hangi siparişlerin sorunlu olduğunu hızla bulmak gerekir:
-- Belirli tarih aralığında, belirli durumda olan siparişler
SELECT s.siparis_id, s.urun_adi, s.miktar, s.fiyat,
s.siparis_tarihi, s.durum
FROM siparisler s
WHERE s.durum IN ('beklemede', 'iptal')
AND s.siparis_tarihi BETWEEN '2024-01-01' AND '2024-03-31'
AND s.fiyat > 1000
ORDER BY s.siparis_tarihi DESC;
-- Toplam etkilenen tutarı hesapla
SELECT durum,
COUNT(*) as siparis_sayisi,
SUM(fiyat * miktar) as toplam_tutar
FROM siparisler
WHERE siparis_tarihi BETWEEN '2024-01-01' AND '2024-03-31'
AND durum IN ('beklemede', 'iptal')
GROUP BY durum;
WHERE ile Performans Optimizasyonu
WHERE koşulları performansı doğrudan etkiler. Birkaç kritik nokta:
- İndexli sütunlara göre filtrele: WHERE koşulunda kullandığınız sütunlara index ekleyin
- Fonksiyon kullanımından kaçının:
WHERE YEAR(ise_giris_tarihi) = 2020index kullanamaz, bunun yerineWHERE ise_giris_tarihi BETWEEN '2020-01-01' AND '2020-12-31'kullanın - Wildcard’ı başa koymayın:
WHERE ad LIKE '%met'index kullanamaz - Tip uyumsuzluğundan kaçının:
WHERE id = '5'gibi string ile integer karşılaştırması implicit cast yapar ve yavaş çalışabilir
-- Kötü kullanım (index kullanamaz)
SELECT * FROM calisanlar WHERE YEAR(ise_giris_tarihi) = 2020;
SELECT * FROM calisanlar WHERE maas + 1000 > 9000;
-- İyi kullanım (index kullanabilir)
SELECT * FROM calisanlar
WHERE ise_giris_tarihi BETWEEN '2020-01-01' AND '2020-12-31';
SELECT * FROM calisanlar WHERE maas > 8000;
-- EXPLAIN ile sorgu planını inceleyin
EXPLAIN SELECT * FROM calisanlar WHERE departman = 'IT' AND maas > 8000;
-- Index ekleyin
CREATE INDEX idx_departman ON calisanlar(departman);
CREATE INDEX idx_departman_maas ON calisanlar(departman, maas);
WHERE Koşulunda Dikkat Edilmesi Gerekenler
Deneyimlerimden derlediğim kritik noktalar:
- Boş string ile NULL farklıdır:
WHERE sehir = ''ileWHERE sehir IS NULLfarklı sonuçlar döndürür. Boş string olan kayıtları NULL’la karıştırmayın.
- Floating point karşılaştırmaları tehlikeli olabilir:
WHERE fiyat = 9.99bazen beklenmedik sonuçlar üretir. Bunun yerineWHERE fiyat BETWEEN 9.98 AND 10.00veya DECIMAL tipi kullanın.
- UPDATE ve DELETE’ten önce SELECT çalıştırın: Her zaman önce SELECT ile kaç satırın etkileneceğini görün. Bu alışkanlık sizi birçok felaketten kurtarır.
- Büyük tablolarda LIMIT kullanın: Test sorgularında
LIMIT 100ekleyin, özellikle WHERE koşulundan emin değilseniz.
- Transaction kullanın: Kritik güncellemeleri transaction içinde yapın, hata durumunda ROLLBACK ile geri alabilirsiniz.
-- Güvenli güncelleme örneği
START TRANSACTION;
UPDATE calisanlar
SET aktif = 0
WHERE departman = 'Pazarlama'
AND ise_giris_tarihi < '2020-01-01';
-- Etkilenen satırları kontrol et
SELECT ROW_COUNT() as etkilenen_satir;
-- Sonucu gör
SELECT id, ad, soyad, aktif FROM calisanlar
WHERE departman = 'Pazarlama';
-- Her şey doğruysa
COMMIT;
-- Hata varsa
-- ROLLBACK;
Sonuç
WHERE koşulu SQL’in bel kemiğidir. Basit görünse de doğru kullanmak ciddi fark yaratır. Performanslı bir veritabanı yönetimi için WHERE koşullarını index kullanımıyla birlikte düşünmek, NULL değerlerine dikkat etmek ve her zaman önce SELECT ile test etmek olmazsa olmaz alışkanlıklardır.
Özellikle üretim ortamında çalışıyorsanız şu altın kuralı unutmayın: WHERE olmadan UPDATE ve DELETE asla. MySQL’de SET sql_safe_updates = 1; komutunu çalıştırarak WHERE koşulsuz UPDATE ve DELETE’lere karşı güvenlik önlemi aktif edebilirsiniz. Bu, yanlışlıkla tüm tabloyu silmenizi veya güncellemenizi önler.
WHERE koşulunu iyi anladıktan sonra HAVING, JOIN koşulları ve subquery’lerle birleştirerek çok daha güçlü sorgular yazabilirsiniz. Bir sonraki yazıda GROUP BY ve HAVING kombinasyonunu ele alacağız.
