Partial Index Sadece Bazı Satırları Kapsar
Normal bir indeks, tablodaki her satır için bir kayıt tutar. Partial index (kısmi indeks) ise yalnızca, indeksi oluştururken verdiğiniz WHERE koşuluna uyan satırları kapsar. Sonuç: daha küçük bir indeks, gezilecek daha az sayfa ve indekslenen kısmı etkilemeyen her INSERT ile UPDATE işleminde daha az yük.
Söz dizimi, sonuna WHERE eklenmiş normal bir CREATE INDEX'ten ibaret:
idx_orders_pending yalnızca status = 'pending' koşulunu sağlayan satırlar için kayıt tutar. Gönderilen, iptal edilen ya da iadesi yapılan siparişler bu indekste hiç yer almaz. Diyelim ki orders tablonuzun %95'i geçmiş kayıtlardan oluşuyor ve siz çoğunlukla açık siparişleri sorguluyorsunuz; aynı sorgu hızı için 20 kat daha küçük bir indeksten bahsediyoruz.
Planlayıcı Bu İndeksi Ne Zaman Gerçekten Kullanır?
Bir kısmi indeks (partial index), ancak SQLite sorgunuzun indeksin kapsadığı satırlarla aynı kümeyi hedeflediğini kanıtlayabildiğinde devreye girer. Bunu sağlamanın en temiz yolu, indeksteki WHERE ifadesini sorguda da birebir tekrarlamaktır:
Plan çıktısında USING INDEX idx_orders_pending görmelisiniz. Sorgudan status = 'pending' koşulunu çıkardığınız an planlayıcı tam tablo taramasına döner — çünkü sorgunun indekslenmiş alt kümede kaldığını anlamasının bir yolu yoktur.
Pratik kural şu: sorgunun WHERE koşulu, indeksin WHERE koşulunu kapsamalı. Aynı sütun ve değer üzerinde eşitlik en güvenli ve apaçık durumdur. Eşitsizlikler ve OR işin içine girince işler karışır; mutlaka EXPLAIN QUERY PLAN ile doğrula.
Neden Uğraşalım — Üç Büyük Kazanç
SQLite kısmi indeks kullanımının üç somut getirisi var:
- Diskte daha küçük yer kaplar. Sadece eşleşen satırlar saklanır. "Tablonun %1'i sıcak" tarzı bir senaryoda, indeks tam halinin yaklaşık %1'i kadar olur.
- Yazma maliyeti düşer.
INSERTveUPDATEişlemleri yalnızca satır filtreye uyduğunda indekse dokunur. Yukarıdaki tabloyastatus = 'shipped'ile yapılan bir eklemeidx_orders_pendingindeksine hiç değmez. - Aynı sorgu hızı. B-tree araması indeks boyutuna göre logaritmiktir. Küçük indeks, biraz daha hızlı arama demektir; ama asıl kazanç çevresinde olur — daha az cache miss, daha az I/O.
Bir sütun çok dengesiz dağılıyorsa — satırların büyük çoğunluğu aynı değere sahip, seni asıl ilgilendiren ise nadir görülen diğer değerlerse — bu tam anlamıyla kitabına uygun bir kısmi indeks senaryosudur.
Partial Unique Index — İşin Cevheri
Standart UNIQUE kısıtı her satıra uygulanır. Soft delete (yumuşak silme) mantığını projene kattığınız anda bu bir soruna dönüşür:
-- Başarısız olur: email = 'a@x.com' olan iki satır var, biri silinmiş olsa bile.
CREATE UNIQUE INDEX idx_users_email ON users(email);
Bir partial unique index sayesinde benzersizlik kuralını yalnızca işinize yarayan satırlar üzerinde zorunlu kılabilirsiniz:
Üç satır, aynı e-posta, hiçbir kısıt ihlali yok — çünkü teklik kontrolüne yalnızca deleted_at IS NULL koşulunu sağlayan satır dahil oluyor. Aynı e-posta ile ikinci bir aktif satır eklemeye kalkıştığınızda SQLite size UNIQUE constraint failed hatasını fırlatır.
Bu desen her yerde karşınıza çıkar: müşteri başına tek aktif abonelik, kullanıcı başına tek birincil adres, sipariş başına tek açık fatura. SQLite kısmi indeks (partial index) kullanımı tam da bu senaryoları doğrudan ifade etmeyi sağlar.
NULL Değerlerle İndeksleme
NULL, indekslerle biraz tuhaf bir ilişkiye sahiptir. Sık karşılaşılan bir ihtiyaç şu: "NULL'ları tamamen yok say." Örneğin, çoğu satırı NULL olan ama dolu olanların benzersiz olması gereken seyrek bir external_id sütununuz olduğunu düşünün:
İki NULL değer sorunsuz bir şekilde yan yana durabilir; EXT-001 ve EXT-002 kayıtlarının ise tekil olması garanti altındadır. Üstelik bu indeks daha küçüktür — NULL satırlar hiç saklanmaz — bu yüzden tablo büyüse bile external_id üzerinden yapılan aramalar hızlı kalır.
Filtre İçinde Neler Kullanılabilir
Kısmi indekste yer alan WHERE koşulu oldukça kısıtlıdır. Şunlara referans verebilir:
- İndekslenen tablonun kolonlarına.
- Sabit (literal) değerlere.
- Belirli (deterministik) yerleşik fonksiyonların sınırlı bir kümesine.
Şunlara referans veremez:
- Başka tablolara.
- Alt sorgulara (subquery).
random()veyaCURRENT_TIMESTAMPgibi deterministik olmayan fonksiyonlara.- Parametre ya da değişkenlere.
Bu kısıtlama mantıklı — SQLite, her INSERT ve UPDATE işleminde filtreyi tekrar değerlendirmek zorunda ve sonucun her seferinde aynı olması gerekir. Yani şu örnek sorunsuz çalışır:
Ama WHERE created_at > date('now') çalışmaz — çünkü date('now') zamanla değişir ve indekslenen satır kümesi SQLite'ın altından kayıp gider.
Sağlama Yapma Akışı
Bir kısmi indeks eklediğinde, şu üç adımı sırayla kontrol et:
Sorgu 1 idx_jobs_runnable indeksini kullanmalı. Sorgu 2 ve 3 ise tarama (scan) yapmaya ya da varsa başka bir indekse düşmeli. Planlayıcı, beklemediğiniz bir sorguda kısmi indeksi seçerse filtrenizi tekrar gözden geçirin — düşündüğünüzden daha geniş kapsıyor olabilir.
Ne zaman kullanmamalı
SQLite kısmi indeks (partial index) keskin bir araçtır. Şu durumlarda uzak durun:
- Filtre tablonun büyük kısmını kapsıyorsa. Satırların %90'ı "active" ise, kısmi indeks aslında fazladan iş yükü olan sıradan bir indeksten ibarettir. Direkt sütunu indeksleyin.
- Sorgularınız filtreyi birebir içermiyorsa. Kodunuz bir ORM üzerinden
WHERE status IN (?, ?, ?)üretiyorsa veya filtreyi dinamik olarak hesaplıyorsa, planlayıcı çoğu zaman eşleşmeyi yakalayamaz. Varsaymayın,EXPLAIN QUERY PLANile test edin. - Sıcak alt küme zamanla kayıyorsa. "Son 30 gündeki siparişler" üzerinde bir kısmi indeks kulağa hoş gelir ama bu ifade edilemez — filtrenin deterministik olması gerekir. Bunun yerine indeksi periyodik olarak yeniden oluşturmanız ya da farklı bir şema seçmeniz gerekir (ayrı bir
recent_orderstablosu veya gece çevirdiğiniz birarchivedboolean alanı gibi).
Filtre sabitse ve büyük bir tablonun küçük bir dilimine denk geliyorsa, kısmi indeksler SQLite'ta yapabileceğiniz en yüksek getirili optimizasyonlardan biridir.
Sırada: Sorgu Planlarını Okumak
Bu sayfanın büyük bölümünde, indeksin gerçekten kullanılıp kullanılmadığını doğrulamak için EXPLAIN QUERY PLAN komutuna güvendik. Bu araç kendi başına bir sayfayı hak ediyor — çıktısını nasıl okuyacağınız, anahtar kelimelerin ne anlama geldiği ve mutlu bir indeks aramasıyla sinsi bir tam tablo taraması arasındaki farkı nasıl yakalayacağınız. Sırada o var.
Sıkça Sorulan Sorular
SQLite'ta partial index nedir?
Partial index, oluşturulurken verdiğiniz WHERE koşuluna uyan satırları indeksleyen bir indekstir. CREATE INDEX name ON table(col) WHERE condition yazdığınızda SQLite yalnızca koşulu sağlayan satırlar için kayıt tutar. Sonuç: daha küçük indeks, daha hızlı yazma ve filtreyle eşleşen sorgular için aynı arama performansı.
Tam indeks yerine ne zaman partial index kullanmalıyım?
Büyük bir tablonun küçük bir dilimini sürekli sorguluyorsanız: bekleyen siparişler, aktif kullanıcılar, işlenmemiş job'lar gibi. Sadece o dilimi indekslemek indeksi minik tutar ve diğer satırlara yapılan yazmalar indeksi hiç güncellemez. Ancak sorgularınız indeksin WHERE koşulunu içermiyorsa planlayıcı bu indeksi kullanamaz.
Partial index ile unique kısıtı uygulanabilir mi?
Evet. CREATE UNIQUE INDEX ... WHERE ... yalnızca filtreye uyan satırlar üzerinde benzersizliği zorlar. Klasik kullanımı 'kullanıcı başına tek aktif kayıt'tır: soft delete edilmiş satırlar dışarıda kaldığı için aynı anahtara sahip birden fazla silinmiş kayıt olabilir, ama canlı sadece bir tane bulunur.