MariaDB ve MySQL’de LIKE Operatörü ile Metin Arama Sorguları

Veritabanı yönetiminde en sık ihtiyaç duyulan işlemlerden biri, tablo içindeki verileri belirli bir pattern’e göre filtrelemektir. Tam eşleşme yerine kısmi eşleşme aradığınızda, yani “şununla başlayan”, “şunu içeren” veya “şununla biten” gibi sorgular yazmak istediğinizde LIKE operatörü devreye girer. MySQL ve MariaDB’de LIKE, özellikle kullanıcı arama sistemleri, log filtreleme, ürün katalogları ve içerik yönetim sistemlerinde günlük olarak kullandığınız bir yapıdır. Bu yazıda LIKE operatörünü her açıdan ele alacak, gerçek dünya senaryolarıyla pekiştireceğiz.

LIKE Operatörü Nedir ve Nasıl Çalışır?

LIKE operatörü, WHERE koşulunda metin sütunlarını joker karakterlerle karşılaştırmak için kullanılır. İki temel joker karakter vardır:

  • %: Sıfır veya daha fazla herhangi bir karakteri temsil eder
  • _: Tam olarak bir karakteri temsil eder

Bu iki karakter kombinasyonuyla oldukça esnek arama sorguları yazabilirsiniz. Sözdizimi son derece basittir:

SELECT kolon_adi FROM tablo_adi WHERE kolon_adi LIKE 'pattern';

MySQL ve MariaDB’de LIKE operatörü varsayılan olarak büyük/küçük harf duyarsızdır (case-insensitive), yani LIKE 'admin' ile LIKE 'ADMIN' aynı sonucu döndürür. Bu davranış sütunun collation ayarına bağlıdır. Eğer büyük/küçük harf duyarlı arama yapmak istiyorsanız BINARY anahtar kelimesini kullanmanız gerekir, bunu ilerleyen bölümlerde göreceğiz.

Temel Kullanım Örnekleri

% Joker Karakteri ile Arama

En yaygın senaryo ile başlayalım. Diyelim ki bir e-ticaret sitesinin veritabanında ürün tablosu var ve “telefon” kelimesini içeren tüm ürünleri bulmak istiyorsunuz:

-- "telefon" içeren tüm ürünler
SELECT urun_id, urun_adi, fiyat FROM urunler
WHERE urun_adi LIKE '%telefon%';

-- Sonuç örneği:
-- Samsung Galaxy Telefon Kılıfı
-- iPhone Telefon Tutucu
-- Telefon Şarj Kablosu
-- Akıllı Telefon Ekran Koruyucu

Burada %telefon% pattern’i, “telefon” kelimesinin önünde ve arkasında herhangi bir karakter dizisi olabileceği anlamına gelir. Sadece başından aramak veya sadece sonundan aramak isterseniz:

-- "Samsung" ile başlayan ürünler
SELECT urun_adi, kategori FROM urunler
WHERE urun_adi LIKE 'Samsung%';

-- ".jpg" ile biten dosya yolu kayıtları
SELECT dosya_adi, yukleme_tarihi FROM medya_dosyalari
WHERE dosya_yolu LIKE '%.jpg';

_ Joker Karakteri ile Tam Uzunluk Araması

Alt çizgi karakteri daha spesifik senaryolarda işe yarar. Örneğin, ülke kodu gibi sabit uzunlukta verilerde veya belirli bir format kontrolünde kullanabilirsiniz:

-- TR ile başlayan, ardından tam 8 karakter gelen telefon numaraları
SELECT musteri_adi, telefon FROM musteriler
WHERE telefon LIKE 'TR________';

-- 3 karakterli ülke kodları
SELECT ulke_kodu, ulke_adi FROM ulkeler
WHERE ulke_kodu LIKE '___';

-- "A" ile başlayan 5 harfli ürün kodları
SELECT urun_kodu, urun_adi FROM urunler
WHERE urun_kodu LIKE 'A____';

Gerçek Dünya Senaryoları

Senaryo 1: Kullanıcı Arama Sistemi

Bir CRM uygulamasında müşteri adına göre arama yapmanız gerekiyor. Kullanıcı arama kutusuna “ahmet” yazdığında, “Ahmet”, “Mehmet”, “Ahmet Ali” gibi sonuçları getirmek istiyorsunuz:

-- Kullanıcı girişine göre dinamik arama (PHP/uygulama tarafında
-- parametreli sorgu kullanmayı unutmayın!)
SELECT
    musteri_id,
    ad,
    soyad,
    email,
    telefon
FROM musteriler
WHERE
    ad LIKE '%ahmet%'
    OR soyad LIKE '%ahmet%'
    OR CONCAT(ad, ' ', soyad) LIKE '%ahmet%'
ORDER BY soyad, ad;

Bu sorguyu production ortamında kullanırken mutlaka parametreli sorgular kullanın. Ham string birleştirmesiyle SQL injection açığı yaratabilirsiniz.

Senaryo 2: Log Dosyası Analizi

Uygulama loglarını veritabanında saklıyorsanız (ki bu yaygın bir pratiktir), belirli hata mesajlarını filtrelemek için LIKE son derece kullanışlıdır:

-- Son 24 saatte "connection refused" içeren log kayıtları
SELECT
    log_id,
    log_seviyesi,
    log_mesaji,
    sunucu_ip,
    olusturma_zamani
FROM uygulama_loglari
WHERE
    log_mesaji LIKE '%connection refused%'
    AND olusturma_zamani >= NOW() - INTERVAL 24 HOUR
ORDER BY olusturma_zamani DESC;

-- ERROR ile başlayan tüm kritik logları say
SELECT
    DATE(olusturma_zamani) AS tarih,
    COUNT(*) AS hata_sayisi
FROM uygulama_loglari
WHERE log_mesaji LIKE 'ERROR%'
GROUP BY DATE(olusturma_zamani)
ORDER BY tarih DESC
LIMIT 30;

Senaryo 3: E-posta Domain Filtreleme

Belirli bir domain’e sahip kullanıcıları bulmak, bulk e-posta operasyonlarında veya domain bazlı kısıtlamalar uygularken çok işe yarar:

-- Gmail kullanıcılarını bul
SELECT kullanici_adi, email, kayit_tarihi
FROM kullanicilar
WHERE email LIKE '%@gmail.com';

-- Kurumsal e-posta kullanmayan kullanıcıları bul
-- (ücretsiz servisler listesi)
SELECT kullanici_adi, email FROM kullanicilar
WHERE
    email LIKE '%@gmail.com'
    OR email LIKE '%@hotmail.com'
    OR email LIKE '%@yahoo.com'
    OR email LIKE '%@outlook.com';

-- Belirli bir şirketin tüm çalışanları
SELECT ad, soyad, email, departman
FROM personel
WHERE email LIKE '%@sirketadi.com.tr'
ORDER BY departman, soyad;

LIKE ile NOT LIKE Kullanımı

LIKE’ın tersini almak için NOT LIKE kullanırsınız. Bu özellikle belirli pattern’leri dışlamak istediğinizde işe yarar:

-- Test kullanıcılarını dışla
SELECT kullanici_adi, email, son_giris
FROM kullanicilar
WHERE
    kullanici_adi NOT LIKE '%test%'
    AND kullanici_adi NOT LIKE '%demo%'
    AND email NOT LIKE '%@test.com'
ORDER BY son_giris DESC;

-- Belirli bir prefix ile başlamayan ürün kodlarını bul
SELECT urun_kodu, urun_adi, stok_miktari
FROM urunler
WHERE urun_kodu NOT LIKE 'ARCH_%'
AND stok_miktari > 0;

BINARY ile Case-Sensitive Arama

Varsayılan olarak case-insensitive olan LIKE’ı büyük/küçük harf duyarlı hale getirmek için BINARY anahtar kelimesini kullanırsınız:

-- Case-sensitive arama: sadece küçük harfli "admin" bulur
SELECT kullanici_adi, email FROM kullanicilar
WHERE kullanici_adi LIKE BINARY 'admin%';

-- "Admin", "ADMIN" gibi varyasyonları dışarıda bırakır
-- Sadece "admin", "administrator" gibi küçük harfle başlayanları getirir

-- Karşılaştırma: case-insensitive vs sensitive
-- Bu sorgu "Admin", "ADMIN", "admin" hepsini getirir:
SELECT kullanici_adi FROM kullanicilar
WHERE kullanici_adi LIKE 'admin%';

-- Bu sorgu sadece "admin" ile başlayanları getirir:
SELECT kullanici_adi FROM kullanicilar
WHERE kullanici_adi LIKE BINARY 'admin%';

Joker Karakterlerin Escape Edilmesi

Peki ya arama yapmak istediğiniz metinde % veya _ karakteri varsa? Örneğin “%50 indirim” ifadesini içeren kayıtları arıyorsunuz. Bu durumda ESCAPE anahtar kelimesini kullanmanız gerekir:

-- Yüzde işareti içeren kampanya isimlerini ara
SELECT kampanya_adi, baslangic_tarihi, bitis_tarihi
FROM kampanyalar
WHERE kampanya_adi LIKE '%%%' ESCAPE '';

-- Alt çizgi içeren ürün kodlarını ara
-- Örneğin "ABC_123" formatındaki kodları bulmak için
SELECT urun_kodu, urun_adi FROM urunler
WHERE urun_kodu LIKE '%_%' ESCAPE '';

-- Özel escape karakteri belirleyerek kullanım
SELECT aciklama FROM notlar
WHERE aciklama LIKE '%!%%' ESCAPE '!';

LIKE ile JOIN Kullanımı

LIKE’ı JOIN sorgularıyla birleştirdiğinizde daha güçlü sorgular yazabilirsiniz:

-- Kategori adı "elektronik" içeren kategorilerdeki ürünleri getir
SELECT
    u.urun_id,
    u.urun_adi,
    u.fiyat,
    k.kategori_adi,
    m.marka_adi
FROM urunler u
INNER JOIN kategoriler k ON u.kategori_id = k.kategori_id
INNER JOIN markalar m ON u.marka_id = m.marka_id
WHERE
    k.kategori_adi LIKE '%elektronik%'
    AND u.aktif = 1
ORDER BY u.fiyat ASC;

-- İstanbul'daki müşterilerin siparişlerini bul
SELECT
    s.siparis_id,
    CONCAT(m.ad, ' ', m.soyad) AS musteri_adi,
    m.sehir,
    s.toplam_tutar,
    s.siparis_tarihi
FROM siparisler s
INNER JOIN musteriler m ON s.musteri_id = m.musteri_id
WHERE
    m.adres LIKE '%İstanbul%'
    AND s.siparis_tarihi >= '2024-01-01'
ORDER BY s.siparis_tarihi DESC;

Performans Konuları ve LIKE Optimizasyonu

Bu kısım birçok sysadmin ve DBA’in gözden kaçırdığı kritik bir noktadır. LIKE operatörü yanlış kullanıldığında index’leri bypass eder ve full table scan yapar. Bunu anlamak production performansı için hayati önem taşır.

Index Kullanımı Açısından LIKE Pattern’leri

  • LIKE 'samsung%': Başından sabit bir string ile başlayan pattern, index kullanır (range scan). En verimli kullanım budur.
  • LIKE '%samsung%': Başında % olan pattern, index kullanamaz (full table scan). Büyük tablolarda ciddi performans sorununa yol açar.
  • LIKE '%samsung': Başında % olan pattern, index kullanamaz.

Bunu EXPLAIN ile doğrulayabilirsiniz:

-- Index kullanımını kontrol et
EXPLAIN SELECT urun_adi, fiyat FROM urunler
WHERE urun_adi LIKE 'Samsung%';

-- Full table scan'i tespit et
EXPLAIN SELECT urun_adi, fiyat FROM urunler
WHERE urun_adi LIKE '%Samsung%';

-- urun_adi sütununa index ekle (başından arama için faydalı)
ALTER TABLE urunler ADD INDEX idx_urun_adi (urun_adi);

-- Büyük tablolarda LIKE '%...%' için FULLTEXT index kullan
ALTER TABLE urunler ADD FULLTEXT INDEX ft_urun_adi (urun_adi);

-- FULLTEXT ile MATCH...AGAINST kullanımı (LIKE yerine)
SELECT urun_adi, fiyat FROM urunler
WHERE MATCH(urun_adi) AGAINST('samsung' IN BOOLEAN MODE);

LIKE Sorgularında Pratik Performans İpuçları

  • Başından arama yapabiliyorsanız LIKE 'prefix% formatını tercih edin, bu index’i kullanır.
  • Orta veya sondaki eşleşmeler için FULLTEXT index düşünün.
  • Çok sık çalışan büyük tablo sorguları için sonuçları cache’leyin (Redis/Memcached).
  • Küçük tablolarda (birkaç bin satır) full table scan genellikle sorun değildir.
  • WHERE LIKE koşulunu diğer indeksli koşullarla birleştirin, böylece önce indeksli filtreleme yapılır, LIKE az sayıda satıra uygulanır.
-- Kötü performans: tüm tabloyu tarar
SELECT * FROM siparisler WHERE notlar LIKE '%acil%';

-- İyi performans: önce tarih filtresi (indeksli) uygulanır,
-- sonra LIKE az sayıda satıra uygulanır
SELECT * FROM siparisler
WHERE
    siparis_tarihi >= '2024-01-01'
    AND musteri_id = 12345
    AND notlar LIKE '%acil%';

LIKE ile Aggregate Fonksiyonlar

LIKE’ı COUNT, SUM gibi aggregate fonksiyonlarla birleştirerek istatistiksel sorgular da yazabilirsiniz:

-- Domain bazlı kullanıcı sayısı raporu
SELECT
    SUBSTRING_INDEX(email, '@', -1) AS domain,
    COUNT(*) AS kullanici_sayisi
FROM kullanicilar
WHERE
    email LIKE '%@%.%'
    AND aktif = 1
GROUP BY SUBSTRING_INDEX(email, '@', -1)
HAVING kullanici_sayisi > 10
ORDER BY kullanici_sayisi DESC;

-- "İade" içeren sipariş notlarının toplam tutarı
SELECT
    COUNT(*) AS iade_siparis_sayisi,
    SUM(toplam_tutar) AS toplam_iade_tutari,
    AVG(toplam_tutar) AS ortalama_iade_tutari
FROM siparisler
WHERE
    siparis_notu LIKE '%iade%'
    AND siparis_tarihi >= DATE_SUB(NOW(), INTERVAL 3 MONTH);

Çoklu Pattern ile Gelişmiş Arama

Birden fazla LIKE koşulunu OR/AND ile birleştirerek karmaşık arama senaryoları oluşturabilirsiniz:

-- Çoklu anahtar kelime araması
SELECT
    icerik_id,
    baslik,
    yazar,
    yayin_tarihi
FROM icerikler
WHERE
    (
        baslik LIKE '%mysql%'
        OR baslik LIKE '%mariadb%'
        OR icerik LIKE '%veritabani%'
        OR etiketler LIKE '%sql%'
    )
    AND durum = 'yayinda'
    AND yayin_tarihi >= '2023-01-01'
ORDER BY yayin_tarihi DESC
LIMIT 20;

-- Hem belli bir pattern içeren hem de içermeyen kayıtlar
SELECT urun_kodu, urun_adi, kategori
FROM urunler
WHERE
    urun_adi LIKE '%kablosuz%'
    AND urun_adi NOT LIKE '%kılıf%'
    AND urun_adi NOT LIKE '%aksesuar%'
    AND stok_miktari > 0;

MariaDB’ye Özgü: REGEXP ile Karşılaştırma

Bazı durumlarda LIKE yetmez ve daha güçlü pattern matching’e ihtiyaç duyarsınız. MariaDB ve MySQL’de REGEXP (veya RLIKE) operatörü bu ihtiyacı karşılar:

-- LIKE ile: "05" ile başlayan telefon numaraları
SELECT musteri_adi, telefon FROM musteriler
WHERE telefon LIKE '05%';

-- REGEXP ile: daha esnek telefon format kontrolü
SELECT musteri_adi, telefon FROM musteriler
WHERE telefon REGEXP '^0[0-9]{10}$';

-- Geçerli e-posta format kontrolü (basit)
SELECT email FROM kullanicilar
WHERE email REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$';

REGEXP LIKE’tan daha güçlü olsa da genellikle daha yavaştır. Basit pattern’ler için LIKE tercih edilmelidir.

Güvenlik: SQL Injection ve LIKE

LIKE sorgularında SQL injection riski özellikle önemlidir. Uygulamanızda kullanıcıdan aldığınız girdiyi doğrudan sorguya koymayın:

-- YANLIS yöntem (PHP örneği ile açıklama için SQL olarak gösteriyoruz):
-- WHERE urun_adi LIKE '%' + kullanici_girdisi + '%'
-- Kullanıcı "%" veya "_" girerse beklenmedik sonuçlar alırsınız
-- Kullanıcı "'; DROP TABLE urunler; --" girerse felaket olur

-- DOGRU yöntem: Uygulama tarafında her zaman parametreli sorgu kullanın
-- PHP PDO örneği:
-- $stmt = $pdo->prepare("SELECT * FROM urunler WHERE urun_adi LIKE ?");
-- $arama = '%' . $kullanici_girdisi . '%';
-- $stmt->execute([$arama]);

-- MariaDB/MySQL stored procedure içinde güvenli kullanım:
DELIMITER //
CREATE PROCEDURE UrunAra(IN p_arama VARCHAR(100))
BEGIN
    SET @sorgu = CONCAT('%', p_arama, '%');
    SELECT urun_id, urun_adi, fiyat
    FROM urunler
    WHERE urun_adi LIKE @sorgu;
END //
DELIMITER ;

-- Çağırma:
CALL UrunAra('telefon');

Sonuç

LIKE operatörü, MySQL ve MariaDB’de metin aramaları için vazgeçilmez bir araçtır. % ve _ joker karakterleriyle oldukça esnek pattern’ler oluşturabilir, NOT LIKE ile ters filtreleme yapabilir, BINARY ile case-sensitive aramaya geçebilirsiniz.

Ancak en kritik nokta performanstır. Başında % bulunan pattern’ler index kullanamaz ve büyük tablolarda ciddi yavaşlamalara neden olur. Production ortamında sık çalışan ve büyük tablolara dokunan LIKE '%metin%' sorgularınız varsa FULLTEXT index veya Elasticsearch gibi arama motorlarına geçmeyi mutlaka değerlendirin.

Günlük yönetim işlerinizde LIKE’ı bilinçli kullandığınızda, kullanıcı aramaları, log analizleri, veri temizleme ve raporlama görevlerinde hem yazmaya hem de anlamaya kolay, güçlü sorgular elde edersiniz. EXPLAIN ile sorgu planınızı her zaman kontrol etmeyi ve uygulama tarafında parametreli sorguları kullanmayı ihmal etmeyin.

Bir yanıt yazın

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