SQLite'ta metin içinde desen aramak için üç işleç kullanılır: LIKE yüzde (%) ve alt çizgi (_) joker karakterleriyle büyük-küçük harfe duyarsız arar, GLOB Unix dosya adı jokerleriyle (*, ?, [...]) harfe duyarlı arar, REGEXP ise düzenli ifade içindir ama yalnızca bir regexp() fonksiyonu tanımlanmışsa çalışır. Bu fonksiyon standart kütüphanede yoktur; sqlite3 komut satırı aracında ise bir eklenti olarak hazır gelir.

Bu yazıda her işlecin kurallarını, ESCAPE ile joker karakterlerin nasıl aranacağını, Türkçe harflerde LIKE'ın neden "harf duyarsız" olmadığını ve LIKE sorgusunun ne zaman bir indeksi kullanabildiğini örneklerle göreceksiniz. Metni parçalamak ve değiştirmek için kullanılan fonksiyonlar SQLite metin fonksiyonları yazısında, kelime tabanlı tam metin arama ise FTS5 yazısında anlatılıyor.

LIKE işlecinin temel kuralları

LIKE işlecinin temel kuralları

X LIKE Y ifadesinde sağdaki işlenen desendir, soldaki eşleştirilecek metindir. Desende % sıfır veya daha fazla karakterle, _ tam olarak bir karakterle eşleşir. Diğer her karakter kendisiyle veya büyük-küçük harf karşılığıyla eşleşir. Belgenin altını çizdiği kritik kısıt şudur: SQLite varsayılan olarak büyük-küçük harf eşlemesini yalnızca ASCII harfler için bilir. Desende joker yoksa LIKE tüm metnin eşleşmesini ister; yani LIKE 'kablo' yalnızca tam olarak "kablo" olan değeri bulur.

Aşağıdaki örnekler bu yazı boyunca kullanacağımız küçük bir ürün tablosu üzerinde çalışır. Büyük-küçük harfle ilgili sonuçların standart SQLite derlemesine ait olduğunu belirtelim; farklı derlemelerdeki sapmaya aşağıda değineceğiz.

sql
CREATE TABLE urun(id INTEGER PRIMARY KEY, ad TEXT, kod TEXT);
INSERT INTO urun(ad, kod) VALUES
  ('Kablosuz Mouse', 'MS-100'),
  ('kablolu klavye', 'KB-200'),
  ('USB Kablo 1m', 'CB-001'),
  ('Şarj Kablosu', 'CB-002'),
  ('İndirim %20 Paketi', 'PK-020');

SELECT id, ad FROM urun WHERE ad LIKE 'kablo%';
-- çıktı:
-- 1|Kablosuz Mouse
-- 2|kablolu klavye

SELECT id, ad FROM urun WHERE ad LIKE '%kablo%';
-- çıktı:
-- 1|Kablosuz Mouse
-- 2|kablolu klavye
-- 3|USB Kablo 1m
-- 4|Şarj Kablosu

SELECT id, kod FROM urun WHERE kod LIKE 'CB-00_';
-- çıktı:
-- 3|CB-001
-- 4|CB-002

İlk sorgu "kablo" ile başlayan iki ürünü buldu: büyük harfle yazılmış "Kablosuz" da eşleşti, çünkü K harfi ASCII aralığındadır. İkinci sorgudaki baştaki % eşleşmeyi metnin herhangi bir yerine taşıdı. Üçüncü sorguda _ tek bir karakterin yerini tuttu; "CB-00" ile başlayıp ardından tam bir karakter gelen kodlar seçildi.

Türkçe harflerde LIKE neden duyarlı davranır?

Belge örnek olarak 'a' LIKE 'A' ifadesinin doğru, ama ASCII dışındaki 'æ' LIKE 'Æ' ifadesinin yanlış olduğunu verir. Aynı kural Türkçe harflere de uygulanır. Sorun yalnızca ç, ğ, ö, ş, ü ile sınırlı değildir; ı ve İ harfleri daha da sinsi bir sonuç üretir.

sql
SELECT 'a' LIKE 'A', 'ş' LIKE 'Ş', 'i' LIKE 'İ', 'ı' LIKE 'I', 'I' LIKE 'i';
-- çıktı: 1|0|0|0|1

Sonuçları tek tek okuyalım. ş ile Ş eşleşmez. Türkçede birbirinin büyük ve küçük hâli olan i ile İ eşleşmez, ı ile I de eşleşmez. Buna karşılık Türkçede farklı harfler olan I ile i eşleşir, çünkü ikisi de ASCII aralığındadır ve İngilizce kurala göre birbirinin karşılığıdır. Yani büyük harfle yazılmış LIKE 'ILIK' araması "ılık" kaydını bulmaz, ama Türkçede bambaşka bir kelime olan "ilik" kaydını bulur.

Belge bu durum için ICU eklentisini işaret eder: eklenti, tüm Unicode karakterlerde harf katlaması yapan geliştirilmiş bir LIKE sağlar. SSS sayfası ise uygulamanın kendi like() fonksiyonunu ve sıralama kuralını tanımlayarak yerleşik olanların yerine koyabileceğini anlatır. Bu yolların bir bedeli var; bir sonraki bölümlerde göreceğiniz LIKE optimizasyonu, uygulama tanımlı bir like() devredeyken hiçbir zaman uygulanmaz.

ESCAPE: yüzde ve alt çizgi karakterini aramak

Bir metnin içinde gerçek bir % veya _ aramak istediğinizde ESCAPE yan tümcesiyle bir kaçış karakteri belirlersiniz. Bu ifade tek karakterlik bir metin olmalıdır. Desende kaçış karakterinin ardından gelen %, _ veya kaçış karakterinin kendisi düz karakter olarak eşleşir. Ters eğik çizgi yaygın bir seçimdir, ancak SQLite metin sabitlerinde ters eğik çizgi özel anlam taşımadığı için desende tek yazmanız yeterlidir.

sql
SELECT id, ad FROM urun WHERE ad LIKE '%\%%' ESCAPE '\';
-- çıktı: 5|İndirim %20 Paketi

SELECT like('kablo%', 'Kablosuz Mouse'), 'Kablosuz Mouse' NOT LIKE 'kablo%';
-- çıktı: 1|0

SELECT quote(NULL LIKE 'a%'), quote('abc' LIKE NULL), 123 LIKE '12%';
-- çıktı: NULL|NULL|1

SELECT 'a' LIKE 'a' ESCAPE 'xy';
-- hata: ESCAPE expression must be a single character

Bu örnekte üç ayrıntı daha görülüyor. Birincisi, LIKE işleci arka planda like(desen, metin) fonksiyonunu çağırır ve argüman sırası işleçtekinin tersidir: önce desen, sonra metin gelir. ESCAPE kullanıldığında üç argümanlı sürüm çağrılır. İkincisi, işlenenlerden biri NULL ise sonuç NULL olur; bu yüzden WHERE ad NOT LIKE ... koşulu adı NULL olan satırları da dışarıda bırakır. Üçüncüsü, sayılar LIKE'tan önce metne çevrilir: 123 LIKE '12%' doğrudur.

GLOB: harfe duyarlı joker eşleştirme

GLOB: harfe duyarlı joker eşleştirme: Herhangi bir dizi, Tek karakter, Karakter kümesi, Büyük-küçük harf

GLOB LIKE'a benzer ama Unix kabuğundaki dosya adı joker sözdizimini kullanır ve her zaman büyük-küçük harfe duyarlıdır. * sıfır veya daha fazla karakterle, ? tek bir karakterle eşleşir; köşeli parantez bir karakter kümesi tanımlar ve içinde aralık ([0-9]) veya olumsuzlama ([^0]) kullanılabilir. LIKE'ta olduğu gibi glob(desen, metin) fonksiyonunun argüman sırası işlecin tersidir.

sql
SELECT id, ad FROM urun WHERE ad GLOB 'Kablo*';
-- çıktı: 1|Kablosuz Mouse

SELECT id, ad FROM urun WHERE ad GLOB '*[Kk]ablo*';
-- çıktı:
-- 1|Kablosuz Mouse
-- 2|kablolu klavye
-- 3|USB Kablo 1m
-- 4|Şarj Kablosu

SELECT id, kod FROM urun WHERE kod GLOB '[A-C][A-Z]-[0-9][0-9][0-9]';
-- çıktı:
-- 3|CB-001
-- 4|CB-002

SELECT id, kod FROM urun WHERE kod GLOB '*[^0]';
-- çıktı:
-- 3|CB-001
-- 4|CB-002

SELECT glob('*.jpg', 'foto.JPG'), glob('*.jpg', 'foto.jpg'), 'dosya1.txt' GLOB 'dosya?.txt';
-- çıktı: 0|1|1

SELECT 'a*b' GLOB 'a[*]b', 'axb' GLOB 'a[*]b';
-- çıktı: 1|0

Harfe duyarlılık burada bir avantajdır: '*.jpg' deseni "foto.JPG" dosyasını bulmaz. Her iki biçimi yakalamak isterseniz '*.[jJ][pP][gG]' gibi bir küme yazarsınız. Köşeli parantezli kümeler, LIKE ile yapılamayan "bu konumda bir rakam" gibi koşulları ifade etmenin de en basit yoludur; yukarıdaki üçüncü sorgu iki harf, bir tire ve üç rakamdan oluşan kodları seçer. GLOB'un ESCAPE yan tümcesi yoktur; bir joker karakteri düz olarak aramak için onu köşeli parantez içine yazarsınız.

ÖzellikLIKEGLOB
Herhangi bir dizi%*
Tek karakter_?
Karakter kümesiYok[abc], [a-z], [^0-9]
Büyük-küçük harfASCII harflerde duyarsızHer zaman duyarlı
KaçışESCAPE yan tümcesiKarakteri köşeli paranteze almak
İndeks kullanımıNOCASE indeksle (varsayılan mod)BINARY indeksle

REGEXP: komut satırında var, kütüphanede yok

REGEXP işleci SQL dilbilgisinde tanımlıdır, ancak belgeye göre varsayılan olarak arkasında bir fonksiyon yoktur ve kullanıldığında hata verir. Uygulama çalışma anında regexp adlı bir fonksiyon eklerse X REGEXP Y ifadesi regexp(Y, X) çağrısına dönüşür. sqlite3 komut satırı aracı bu konuda farklıdır: belgede listelenen ek özellikler arasında REGEXP işlecine bağlı POSIX genişletilmiş düzenli ifade desteği bulunur ve sürüm geçmişine göre bu eklenti 3.36.0 sürümünden beri komut satırı derlemelerine dahildir.

sql
SELECT 'MS-100' REGEXP '^[A-Z]{2}-[0-9]{3}$', 'ms-100' REGEXP '^[A-Z]{2}-[0-9]{3}$';
-- çıktı: 1|0
SELECT regexp('[0-9]+', 'sipariş 42');
-- çıktı: 1

Aynı sorguyu standart kütüphaneyle çalışan bir uygulamada denediğinizde ise şu hatayı alırsınız. MATCH işleci de benzer bir yer tutucudur; varsayılan match() fonksiyonu hata üretir ve gerçek anlamını FTS5 gibi eklentiler kazandırır.

sql
SELECT 'MS-100' REGEXP '^[A-Z]{2}';
-- hata: no such function: REGEXP
SELECT 'a' MATCH 'a';
-- hata: unable to use function MATCH in the requested context

Bu fark pratikte önemlidir. Komut satırında test edip çalıştığını gördüğünüz bir REGEXP sorgusu, uygulamanızın kullandığı kütüphanede no such function: REGEXP hatası verebilir. Uygulamanızda düzenli ifade gerekiyorsa, kullandığınız dil bağlayıcısıyla bir regexp fonksiyonu kaydetmeniz gerekir; bunun yöntemi bağlayıcıya göre değişir, örneğin Python sqlite3 yazımızda bağlayıcı tarafı ele alınıyor.

LIKE ve GLOB ne zaman indeks kullanır?

Sorgu planlayıcı belirli koşullarda bir LIKE veya GLOB terimini indeks üzerinde bir aralık aramasına çevirebilir. Desenin joker olmayan başlangıç kısmına x dersek, planlayıcı sorguya sütun >= x AND sütun < y biçiminde iki sanal koşul ekler; y, x ile aynı uzunlukta olup x'ten büyük olan en küçük metindir. Bu iyileştirme için belgenin saydığı koşulların başlıcaları şunlardır:

  • Desen bir metin sabiti veya metne bağlanmış bir parametre olmalı ve joker karakterle başlamamalıdır.
  • Sol taraf TEXT affinity'li, indeksli bir sütun olmalı ya da desen eksi işareti veya rakamla başlamamalıdır; çünkü sayılar metin sırasına göre dizilmez.
  • Yerleşik like() ve glob() fonksiyonlarının yerine uygulama tanımlı bir sürüm konmamış olmalıdır.
  • GLOB için sütun BINARY sıralama kuralıyla indekslenmiş olmalıdır.
  • LIKE için, varsayılan harf duyarsız modda sütun NOCASE ile indekslenmiş olmalıdır; harf duyarlı modda BINARY gerekir.
  • ESCAPE kullanılıyorsa kaçış karakteri ASCII veya UTF-8'de tek baytlık bir karakter olmalıdır.
sql
CREATE TABLE kisi(id INTEGER PRIMARY KEY, soyad TEXT);
CREATE INDEX kisi_soyad_nocase ON kisi(soyad COLLATE NOCASE);
CREATE INDEX kisi_soyad ON kisi(soyad);

EXPLAIN QUERY PLAN SELECT id FROM kisi WHERE soyad LIKE 'yıl%';
-- plan: SEARCH kisi USING COVERING INDEX kisi_soyad_nocase (soyad>? AND soyad<?)

EXPLAIN QUERY PLAN SELECT id FROM kisi WHERE soyad LIKE '%maz';
-- plan: SCAN kisi

EXPLAIN QUERY PLAN SELECT id FROM kisi WHERE soyad GLOB 'Yıl*';
-- plan: SEARCH kisi USING COVERING INDEX kisi_soyad (soyad>? AND soyad<?)

Planlardaki (soyad>? AND soyad<?) ifadesi belgede anlatılan aralık dönüşümünün ta kendisidir. LIKE sorgusu NOCASE indeksini, GLOB sorgusu ise BINARY indeksini seçti. Joker karakterle başlayan '%maz' deseni için indeksle başlangıç noktası bulmak mümkün olmadığından tablo baştan sona tarandı. Plan çıktısını nasıl okuyacağınızı EXPLAIN QUERY PLAN yazısında, sıralama kuralı tanımlı indeksleri ise COLLATE yazısında ayrıntılı bulabilirsiniz.

case_sensitive_like pragması: neden kaçınmalısınız?

PRAGMA case_sensitive_like = ON LIKE işlecini ASCII harflerde de harfe duyarlı yapar. Belge bu pragmayı kullanımdan kalkmış (deprecated) olarak işaretler; yeni uygulamaların kullanmamasını, eski uygulamaların ilk fırsatta bırakmasını önerir ve SQLITE_OMIT_DEPRECATED ile derlenen sürümlerde hiç bulunmayabileceğini belirtir.

sql
PRAGMA case_sensitive_like = ON;
SELECT 'a' LIKE 'A', 'a' LIKE 'a';
-- çıktı: 0|1
PRAGMA case_sensitive_like = OFF;
SELECT 'abc' LIKE 'ABC';
-- çıktı: 1

Pragmanın asıl tehlikesi şemayla ilgilidir. Şemanızda LIKE bir CHECK kısıtında, bir ifade indeksinde veya kısmi indeksin WHERE koşulunda geçiyorsa, pragmayla LIKE'ın tanımını değiştirmek veritabanının bozuk görünmesine yol açabilir ve PRAGMA integrity_check hata raporlar. Veri aslında bozuk değildir, ancak kafa karıştırıcı bir durumdur. Harfe duyarlı desen araması gerekiyorsa pragmayı açmak yerine doğrudan GLOB kullanın.

Uygulamalı örnek: kullanıcı girdisiyle güvenli arama

Bir arama kutusuna yazılan metni %...% arasına koyup LIKE ile aramak sık kullanılan bir yöntemdir. Sorun, kullanıcının yazdığı % ve _ karakterlerinin de joker olarak yorumlanmasıdır: "%50" arayan biri "50" içeren her kaydı görür, "kis_2026" arayan biri "kis 2026" kaydını da bulur. Çözüm, girdiyi desene koymadan önce kaçış karakteriyle işaretlemektir.

sql
CREATE TABLE kampanya(id INTEGER PRIMARY KEY, baslik TEXT);
INSERT INTO kampanya(baslik) VALUES
  ('Yaz %50 indirim'), ('Yaz 50 TL indirim'), ('kis_2026 fırsatı'), ('kis 2026 fırsatı');

-- Kullanıcı "%50" yazdı; kaçışsız desen her "50" içeren satırı bulur
SELECT id FROM kampanya WHERE baslik LIKE '%' || '%50' || '%';
-- çıktı:
-- 1
-- 2

-- Önce \ sonra % ve _ kaçışlanır, ESCAPE ile bildirilir
SELECT id FROM kampanya
WHERE baslik LIKE '%' || replace(replace(replace('%50', '\', '\\'), '%', '\%'), '_', '\_') || '%' ESCAPE '\';
-- çıktı: 1

SELECT id FROM kampanya WHERE baslik LIKE '%kis_2026%';
-- çıktı:
-- 3
-- 4

SELECT id FROM kampanya WHERE instr(baslik, 'kis_2026') > 0;
-- çıktı: 3

Kaçış sırası önemlidir: önce kaçış karakterinin kendisi ikilenir, sonra % ve _ işaretlenir; tersini yaparsanız eklediğiniz ters eğik çizgiler yeniden kaçışlanır. Yalnızca "içeriyor mu" sorusuna cevap arıyorsanız ve harf duyarlılığı sorun değilse instr() joker kavramı olmadığı için daha basit bir seçenektir. Gerçek uygulamada '%50' sabiti yerine bir parametre kullanırsınız; girdiyi sorgu metnine eklemeyin, gerekçesi için SQL injection yazımıza bakın.

Sık yapılan hatalar

Sık yapılan hatalar: LIKE'ı Türkçe harflerde harf duyarsız sanmak, Baştaki % ile hızlı arama beklemek, NOCASE olmayan bir indeksle LIKE optimizasyonu beklemek, Komut satırında...
  • LIKE'ı Türkçe harflerde harf duyarsız sanmak: standart derlemede yalnızca ASCII harfler eşlenir; I ile i eşleşir, ş ile Ş eşleşmez.
  • Baştaki % ile hızlı arama beklemek: joker karakterle başlayan desen indeks kullanamaz. Kelime bazlı arama gerekiyorsa FTS5 daha uygun bir araçtır.
  • NOCASE olmayan bir indeksle LIKE optimizasyonu beklemek: varsayılan modda LIKE yalnızca NOCASE indeksten yararlanır.
  • Komut satırında çalışan REGEXP'in uygulamada da çalışacağını varsaymak: fonksiyon kütüphanede tanımlı değildir.
  • like() ve glob() fonksiyonlarına argümanları işleç sırasıyla vermek: fonksiyon biçiminde desen önce gelir.
  • NOT LIKE ile NULL satırları da beklemek: NULL değerler hem LIKE hem NOT LIKE koşulundan elenir; gerekirse OR sütun IS NULL ekleyin. NULL kurallarının tamamı NULL davranışı yazısında.

Sık Sorulan Sorular

SQLite LIKE büyük-küçük harfe duyarlı mı?

Varsayılan olarak ASCII harflerde duyarsız, ASCII dışındaki harflerde duyarlıdır. Bu, SQL standardının LIKE için harf duyarsızlık istemesinden ve SQLite'ın tam Unicode tablolarını varsayılan derlemeye koymamasından kaynaklanır. ASCII harflerde de duyarlı arama istiyorsanız GLOB kullanın.

LIKE mı GLOB mu daha hızlı?

Belge iki işleç arasında genel bir hız sıralaması vermez. Asıl belirleyici, sorgunun indeks kullanıp kullanamadığıdır: önek aramasında LIKE NOCASE indeksle, GLOB BINARY indeksle aralık aramasına dönüşebilir. Uygun indeks yoksa ikisi de tabloyu tarar. Kendi verinizde planı görmek için EXPLAIN QUERY PLAN çalıştırın.

Kelime bazlı arama için LIKE yeterli mi?

Küçük tablolar ve basit "içeriyor mu" sorguları için yeterlidir. Kelime sınırlarına, sıralı sonuçlara, öbek aramaya veya aksan duyarsızlığına ihtiyacınız varsa SQLite'ın FTS5 eklentisi bu iş için tasarlanmıştır; FTS5 tam metin arama yazısında ayrıntılı anlatılıyor.

REGEXP'i uygulamamda nasıl kullanırım?

Kütüphaneye regexp adında, iki argüman alan bir fonksiyon kaydetmeniz gerekir; işleç bu fonksiyonu desen önce gelecek şekilde çağırır. Kayıt işlemi dil bağlayıcısının sağladığı fonksiyon tanımlama arayüzüyle yapılır. Komut satırı aracında ise ek bir işlem gerekmez.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: SQLite öğrenme rehberi · Metin fonksiyonları · COLLATE ve NOCASE · Index oluşturma · EXPLAIN QUERY PLAN · FTS5 tam metin arama