SQLite'ta kısmi indeks (partial index), CREATE INDEX ifadesinin sonuna bir WHERE koşulu eklenerek oluşturulur ve tablonun yalnızca bu koşulu sağlayan satırlarını içerir. İfade indeksi (index on expression) ise sütun adı yerine abs(tutar) veya govde ->> '$.tur' gibi bir ifadeyi indeksler. İki özellik de doğru kullanıldığında daha küçük indeksler ve daha hızlı sorgular sağlar, ancak sorgu planlayıcısı bunları yalnızca sorgudaki koşul ya da ifade indeks tanımıyla belirli kurallara göre eşleştiğinde kullanır.
Bu yazıda iki özelliği, planlayıcının eşleştirme kurallarını, kısmi UNIQUE indeksle "grup başına tek kayıt" kuralını, ifade indekslerinin kısıtlarını, bayat ifade indeksi sorununu ve REINDEX komutunu çalıştırılmış örneklerle ele alıyoruz. Sıradan indekslerin temeli için SQLite index oluşturma, serinin tamamı için SQLite öğrenme rehberi yazılarına bakabilirsiniz.
Kısmi indeks oluşturma

Sıradan bir indekste tablonun her satırı için tam olarak bir girdi bulunur. Kısmi indekste ise yalnızca WHERE koşulunun doğru (true) olduğu satırlar indekslenir; koşul yanlış ya da NULL olan satırlar indekse girmez. Klasik örnek, çoğu satırda NULL olan bir sütundur. Siparişlerin küçük bir kısmının bir üst siparişi olduğunu düşünün:
CREATE TABLE siparis(id INTEGER PRIMARY KEY, ust_id INTEGER, musteri INTEGER, durum TEXT, tutar INTEGER);
CREATE INDEX siparis_ust ON siparis(ust_id) WHERE ust_id IS NOT NULL;
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE ust_id = 42;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH siparis USING COVERING INDEX siparis_ust (ust_id=?)
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE ust_id > 10;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH siparis USING COVERING INDEX siparis_ust (ust_id>?)
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE ust_id IS NULL;
-- çıktı:
-- QUERY PLAN
-- `--SCAN siparis
EXPLAIN QUERY PLAN SELECT id FROM siparis ORDER BY ust_id;
-- çıktı:
-- QUERY PLAN
-- |--SCAN siparis
-- `--USE TEMP B-TREE FOR ORDER BYİndeks yalnızca ust_id değeri dolu olan satırları içerdiği için tablo büyüdükçe tam indeksten çok daha küçük kalır. Tabloya yapılan yazmalar da hızlanır, çünkü ust_id değeri NULL olan satırlar eklenirken veya güncellenirken bu indeksin güncellenmesi gerekmez. Eşitlik ve aralık sorguları indeksi kullandı; IS NULL sorgusu ise kullanamadı, çünkü aradığı satırlar indekste yoktur. Aynı nedenle indeks, tablonun tamamını ust_id sırasıyla döndüren bir ORDER BY için de işe yaramadı.
WHERE koşulunda geçen sütunlar, indekslenen sütunlarla aynı olmak zorunda değildir; tablonun herhangi bir sütunu kullanılabilir. Bir kısmi indeks tablo tanımındaki UNIQUE veya PRIMARY KEY kısıtlarıyla otomatik oluşturulamaz; bunlar her zaman tam indekstir.
Planlayıcı kısmi indeksi ne zaman kullanır
Belgeler kuralı mantık diliyle tanımlar: indeksin koşulu X, sorgunun WHERE koşulu W ise, planlayıcı indeksi ancak W'nin X'i gerektirdiğini (W doğruysa X'in de doğru olduğunu) kanıtlayabildiğinde kullanır. SQLite bunun için genel bir teorem kanıtlayıcı kullanmaz; yalnızca iki basit kural uygular ve bunların dışında kalan her durumu "kullanılamaz" sayar.
Birinci kural: Sorgunun AND ile bağlı terimlerinden biri, indeks koşulunun OR ile bağlı terimlerinden biriyle birebir aynıysa indeks kullanılabilir. Terimler cebirsel olarak değil, yazıldıkları biçimiyle karşılaştırılır:
CREATE INDEX siparis_acik ON siparis(musteri) WHERE durum = 'acik';
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE musteri = 7 AND durum = 'acik';
-- çıktı:
-- QUERY PLAN
-- `--SEARCH siparis USING COVERING INDEX siparis_acik (musteri=?)
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE musteri = 7 AND durum = 'ac' || 'ik';
-- çıktı:
-- QUERY PLAN
-- `--SCAN siparis
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE musteri = 7 AND 'acik' = durum;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH siparis USING COVERING INDEX siparis_acik (musteri=?)
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE musteri = 7;
-- çıktı:
-- QUERY PLAN
-- `--SCAN siparis
EXPLAIN QUERY PLAN SELECT id FROM siparis WHERE musteri = 7 AND durum = ?1;
-- çıktı:
-- QUERY PLAN
-- `--SCAN siparisSorguda durum = 'acik' geçtiğinde indeks kullanıldı. Aynı değeri 'ac' || 'ik' biçiminde yazmak eşleşmeyi bozdu. Belgelere göre b=6 terimi b=3+3, b-6=0 veya b BETWEEN 6 AND 6 ile eşleşmez. Tek istisna eşitliğin taraflarının yer değiştirmesidir: indekste durum = 'acik' varken sorgudaki 'acik' = durum eşleşti. Belgeler bunun tersinin, yani indekste 6=b biçiminde yazılmış bir terimin hiçbir şeyle eşleşmeyeceğini de not eder; indeks koşullarını her zaman sütun solda olacak biçimde yazın.
Son sorgu uygulamalar için önemli bir sonuç taşıyor. Durum değerini ?1 parametresiyle verdiğimizde planlayıcı, hazırlık sırasında değeri bilmediği için indeksin koşulunu kanıtlayamadı ve tabloyu taradı. Kısmi indeksten yararlanmak istediğiniz sorgularda indeks koşulundaki sabiti sorgu metnine doğrudan yazın; kullanıcıdan gelen diğer değerleri ise parametreyle vermeye devam edin. Parametreli sorguların güvenlik açısından önemi için SQL injection önleme yazısına bakın.
İkinci kural: İndeks koşulunda z IS NOT NULL biçiminde bir terim varsa, sorguda z üzerinde IS dışındaki herhangi bir karşılaştırma bu terimle eşleşir. Çünkü =, <, >, <=, >=, <>, IN, LIKE ve GLOB yalnızca z NULL değilken doğru olabilir:
CREATE TABLE t2(a, b, c);
CREATE INDEX t2_bc ON t2(b, c) WHERE c IS NOT NULL;
EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b = 456 AND c <> 0;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH t2 USING INDEX t2_bc (b=?)
EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b = 456 AND c IN (1, 2);
-- çıktı:
-- QUERY PLAN
-- `--SEARCH t2 USING INDEX t2_bc (b=? AND c=?)
EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b = 456;
-- çıktı:
-- QUERY PLAN
-- `--SCAN t2
EXPLAIN QUERY PLAN SELECT * FROM t2 WHERE b = 456 AND c IS 5;
-- çıktı:
-- QUERY PLAN
-- `--SCAN t2c <> 0 ve c IN (1, 2) koşulları indeksi kullanabildi. Yalnızca b = 456 yazıldığında kullanamadı, çünkü b = 456 olup c değeri NULL olan satırlar indekste yoktur. IS işleci NULL ile de doğru sonuç verebildiği için kuralın dışında kalır. Belgeler LIKE işlecini de bu kurala dahil eder ve standart derlemede (SQLite 3.45.3) c LIKE 'a%' koşulu indeksi kullandı. Ancak ICU eklentisiyle derlenmiş deneme aracımızda (3.50.6) LIKE yerleşik işleç yerine ICU'nun fonksiyonuyla çalıştığı için aynı sorgu tabloyu taradı. LIKE'ın derlemeye bağlı davranışları için SQLite LIKE, GLOB ve REGEXP yazısına bakın.
Belgeler bu iki kuralın her zaman geçerli kalacağını, gelecekte daha akıllı bir kanıtlayıcı eklenirse yalnızca daha fazla durumun yakalanabileceğini belirtir. Pratikte en güvenli yol, sorgudaki koşulu indeks tanımındaki metnin aynısıyla yazmak ve sonucu EXPLAIN QUERY PLAN ile doğrulamaktır.
Kısmi UNIQUE indeks: grup başına tek kayıt
Kısmi indeks UNIQUE olarak tanımlanırsa benzersizlik yalnızca indekse giren satırlar arasında denetlenir. Bu, tablonun belirli bir alt kümesinde tekilliği zorlamanın temiz bir yoludur. Belgelerdeki örneğe benzer biçimde, her ekibin yalnızca bir lideri olabilsin:
CREATE TABLE kisi(id INTEGER PRIMARY KEY, ekip INTEGER, ad TEXT, lider INTEGER NOT NULL DEFAULT 0);
CREATE UNIQUE INDEX ekip_lideri ON kisi(ekip) WHERE lider;
INSERT INTO kisi(ekip, ad, lider) VALUES (1, 'Ayşe', 1), (1, 'Mehmet', 0), (1, 'Can', 0), (2, 'Zeynep', 1);
INSERT INTO kisi(ekip, ad, lider) VALUES (1, 'Deniz', 1);
-- hata: UNIQUE constraint failed: kisi.ekip
UPDATE kisi SET lider = 1 WHERE ad = 'Can';
-- hata: UNIQUE constraint failed: kisi.ekip
SELECT ad FROM kisi WHERE lider AND ekip = 1;
-- çıktı: Ayşe
EXPLAIN QUERY PLAN SELECT id FROM kisi WHERE lider AND ekip = 1;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH kisi USING INDEX ekip_lideri (ekip=?)ekip sütunu tek başına benzersiz olamaz, çünkü bir ekipte birçok kişi vardır. (ekip, lider) çifti de benzersiz olamaz, çünkü her ekipte birden fazla lider olmayan kişi bulunur. Kısmi indeks yalnızca liderleri içerdiği için ikinci bir lider eklemek ya da mevcut bir üyeyi lider yapmak engellendi. Aynı indeks, belirli bir ekibin liderini bulan sorguyu da hızlandırıyor. Böyle bir indeksi UPSERT'in çatışma hedefi olarak kullanmak için hedefe indeksin WHERE koşulunu da yazmanız gerekir; örneği SQLite UPSERT yazısında bulabilirsiniz.
Kısmi indeks koşulunun kısıtları
WHERE koşulu işleçler, sabit değerler ve indekslenen tablonun sütunlarını içerebilir. Alt sorgu, başka tablolara başvuru, deterministik olmayan fonksiyon ve bağlı parametre kullanılamaz:
CREATE INDEX hatali1 ON t2(a) WHERE b > (SELECT 1);
-- hata: subqueries prohibited in partial index WHERE clauses
CREATE INDEX hatali2 ON t2(a) WHERE random() > 0;
-- hata: non-deterministic functions prohibited in partial index WHERE clauses
CREATE INDEX hatali3 ON t2(a) WHERE b = ?1;
-- hata: parameters prohibited in partial index WHERE clausesBu kısıtların gerekçesi aynıdır: koşulun sonucu, ilgili satır değişmediği sürece hiç değişmemelidir. Aksi hâlde indeksin içeriği tablodaki veriyle tutarsız hâle gelir.
İfade indeksi

İfade indeksi, CREATE INDEX içinde sütun adı yerine bir ifade yazılarak oluşturulur. Belgelerdeki hesap hareketleri örneğinde para yatırma pozitif, para çekme negatif tutarla kaydedilir ve tutarın büyüklüğüne göre arama yapılmak istenir:
CREATE TABLE hareket(id INTEGER PRIMARY KEY, hesap INTEGER, tutar INTEGER, aciklama TEXT);
CREATE INDEX hareket_buyukluk ON hareket(hesap, abs(tutar));
EXPLAIN QUERY PLAN SELECT * FROM hareket WHERE hesap = 5 AND abs(tutar) >= 10000;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH hareket USING INDEX hareket_buyukluk (hesap=? AND <expr>>?)
EXPLAIN QUERY PLAN SELECT * FROM hareket WHERE hesap = 5 ORDER BY abs(tutar) DESC;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH hareket USING INDEX hareket_buyukluk (hesap=?)
-- Matematiksel olarak aynı koşul, ama ifade indeksle eşleşmiyor
EXPLAIN QUERY PLAN SELECT * FROM hareket
WHERE hesap = 5 AND (tutar >= 10000 OR tutar <= -10000);
-- çıktı:
-- QUERY PLAN
-- `--SEARCH hareket USING INDEX hareket_buyukluk (hesap=?)Plan çıktısında ifade <expr> olarak görünür. İlk sorguda hem hesap numarası hem de abs(tutar) aralığı indeksle karşılandı. İkinci sorguda ORDER BY için ayrı bir sıralama adımı görünmüyor; satırlar indeksten zaten abs(tutar) sırasıyla okunuyor. Üçüncü sorgu aynı mantığı OR ile yazıyor; sonuç matematiksel olarak eşdeğer olsa da ifade indeksin ikinci sütunuyla eşleşmediği için yalnızca hesap sütunu kullanıldı. Belgeler bu indeksin sorguları yalnızca hızlandırdığını, indeks olmadan da aynı sonuçların alınacağını vurgular.
İfade birebir aynı yazılmalı
Planlayıcı, ifade indeksini ancak WHERE veya ORDER BY içindeki ifade, CREATE INDEX içindeki ifadeyle aynı olduğunda kullanır. Boşluk farkları gibi küçük söz dizimi farkları önemsenmez, ama cebirsel dönüşüm yapılmaz:
CREATE TABLE t(x, y, z);
CREATE INDEX t_xy ON t(x + y);
EXPLAIN QUERY PLAN SELECT * FROM t WHERE y + x = 22;
-- çıktı:
-- QUERY PLAN
-- `--SCAN t
EXPLAIN QUERY PLAN SELECT * FROM t WHERE x+y=22;
-- çıktı:
-- QUERY PLAN
-- `--SEARCH t USING INDEX t_xy (<expr>=?)
CREATE TABLE belge(id INTEGER PRIMARY KEY, govde TEXT);
CREATE INDEX belge_tur ON belge(govde ->> '$.tur');
EXPLAIN QUERY PLAN SELECT id FROM belge WHERE govde ->> '$.tur' = 'fatura';
-- çıktı:
-- QUERY PLAN
-- `--SEARCH belge USING COVERING INDEX belge_tur (<expr>=?)
EXPLAIN QUERY PLAN SELECT id FROM belge WHERE json_extract(govde, '$.tur') = 'fatura';
-- çıktı:
-- QUERY PLAN
-- `--SCAN belgey + x ifadesi x + y ile matematiksel olarak eşdeğerdir, fakat indeks kullanılmadı; boşlukları kaldırılmış x+y ise eşleşti. JSON örneği aynı kuralın gündelik bir tuzağını gösteriyor: indeks ->> işleciyle kurulduysa, aynı alanı json_extract() ile okuyan sorgu indeksi kullanmaz. Ekipte JSON alanlarına nasıl erişileceğini tek bir yazım biçimine bağlamak bu yüzden önemlidir. JSON işleçleri için SQLite JSON fonksiyonları yazısına bakın.
İfade indekslerinin kısıtları
İndeksteki ifadeler yalnızca indekslenen tablonun sütunlarına başvurabilir, alt sorgu içeremez ve yalnızca deterministik fonksiyonlar, yani aynı girdiye her zaman aynı çıktıyı veren fonksiyonlar kullanabilir. random() açıkça yasaktır. sqlite_version() tek bir bağlantı boyunca sabit kalsa da veritabanı dosyasının ömrü boyunca değişebileceği için o da kullanılamaz. Uygulamanın tanımladığı SQL fonksiyonları varsayılan olarak deterministik sayılmaz; ancak SQLITE_DETERMINISTIC bayrağıyla kaydedilirlerse indekste kullanılabilirler. İfadeler ayrıca CREATE TABLE içindeki UNIQUE veya PRIMARY KEY kısıtlarında kullanılamaz; ifadeye benzersizlik gerekiyorsa CREATE UNIQUE INDEX yazın.
CREATE INDEX h1 ON hareket(random());
-- hata: non-deterministic functions prohibited in index expressions
CREATE INDEX h2 ON hareket((SELECT 1));
-- hata: subqueries prohibited in index expressions
CREATE INDEX h4 ON hareket(sqlite_version());
-- hata: non-deterministic functions prohibited in index expressions
CREATE TABLE k(a, UNIQUE(abs(a)));
-- hata: expressions prohibited in PRIMARY KEY and UNIQUE constraints
-- 'now' içeren tarih fonksiyonu: hata satır yazılırken çıkar
CREATE TABLE h(a);
CREATE INDEX h3 ON h(date('now'));
INSERT INTO h VALUES (1);
-- hata: non-deterministic use of date() in an indexSon örnek, tarih ve saat fonksiyonlarının özel durumunu gösteriyor. Bu fonksiyonlar normalde deterministik sayılır; ancak 'now' dizesi, localtime veya utc değiştiricisi kullanıldığında deterministik olmaktan çıkarlar. Girdi ancak çalışma anında bilinebildiği için SQLite bu durumu indeks oluşturulurken değil, değer hesaplanırken yakalar: indeks boş tabloda sorunsuz oluştu, ilk satır eklendiğinde hata verdi. Belgelere göre argümansız datetime() biçiminin de "now" anlamına geldiği 3.35.2 sürümüne kadar gözden kaçmıştı; o sürümlerle oluşturulmuş eski veritabanlarında bu tür ifadeler bulunabilir. Ayrıntılar için SQLite tarih ve saat fonksiyonları yazısına bakın.
Bayat ifade indeksi ve kendi kendini onaran indeksler
Deterministik sayılan bir fonksiyon bile farklı işlemci mimarilerinde, işletim sistemlerinde veya SQLite sürümlerinde çok küçük farklarla farklı sonuç verebilir. Bu durumda indekste saklanan değer ile aynı ifadenin şimdi hesaplanan değeri birbirini tutmaz; belgeler buna bayat ifade indeksi (stale expression index) der. Belgelerde sayılan tipik nedenler şunlardır: ondalık sayı üreten matematik fonksiyonlarının sistem kütüphanesine bağlı olması, ICU sürümünün değişmesi, yerleşik fonksiyonlardaki hata düzeltmeleri, kullanımdan kaldırılmış PRAGMA case_sensitive_like ayarının değiştirilmesi ve uygulamanın tanımladığı bir fonksiyonun davranışının güncellenmesi.
Belgelerin verdiği somut örnek, SQLite'ın metinden ondalık sayıya dönüşüm kodunun 3.42'den 3.43'e ve 3.51'den 3.52'ye geçişte değişmesidir. Bu dönüşüm açık bir CAST olmadan da gerçekleşebilir; örneğin JSON belgesinden ->> ile ondalık bir değer okumak bunu tetikler. Böyle bir değer bir ULP (son basamakta bir birim) kadar kayabilir. Belgeler bu sorunun çok nadir görüldüğünü, görüldüğünde de genellikle yalnızca arama aralığının tam sınırındaki bir kaydı etkilediğini belirtir. Eski sürümlerde bayat girdisi olan bir satırı silmek veya güncellemek hata verebilir ve PRAGMA integrity_check bunu raporlar.
SQLite 3.53.0 ile bu durum için kendi kendini onaran indeks özelliği geldi: bayat girdisi olan bir satır silinirken veya güncellenirken SQLite artık hata vermek yerine indeksi düzeltir. PRAGMA integrity_check yine de bu girdileri raporlar; fark bir iki ULP'lik bir ondalık sayı farkıysa mesaj, indeksin bozuk olduğunu söylemek yerine "index NAME stores an imprecise floating-point value for row N" biçiminde olur. Sorunu kökten önlemenin yolu ve onarım komutları aşağıda. Ondalık sayıların genel davranışı için SQLite matematik fonksiyonları, bütünlük denetimi için bozuk veritabanı kurtarma yazılarına bakın.
Alternatif: STORED hesaplanan sütun üzerinde indeks
Bayat indeks ihtimalini tamamen ortadan kaldırmak istiyorsanız belgelerin önerisi, ifadeyi STORED bir hesaplanan sütunda saklamak ve indeksi bu sütuna kurmaktır. Değer satır yazılırken bir kez hesaplanıp tabloda saklandığı için indeks her zaman saklanan değerle tutarlıdır:
CREATE TABLE belge2(
id INTEGER PRIMARY KEY,
govde TEXT,
tur TEXT AS (govde ->> '$.tur') STORED
);
CREATE INDEX belge2_tur ON belge2(tur);
INSERT INTO belge2(govde) VALUES ('{"tur":"fatura"}'), ('{"tur":"irsaliye"}');
EXPLAIN QUERY PLAN SELECT id FROM belge2 WHERE tur = 'fatura';
-- çıktı:
-- QUERY PLAN
-- `--SEARCH belge2 USING INDEX belge2_tur (tur=?)
SELECT id, tur FROM belge2 WHERE tur = 'fatura';
-- çıktı: 1|faturaBu yaklaşımın bir başka yararı da sorgu yazımını sadeleştirmesidir: sorgular tur sütununu doğrudan kullanır ve ifadenin birebir aynı yazılması sorunu ortadan kalkar. Bedeli, değerin tabloda ayrıca yer kaplamasıdır. ALTER TABLE ile STORED sütun eklenemediği için bu düzen mevcut bir tabloda tablo yeniden kurulmasını gerektirir. Hesaplanan sütunların kuralları için SQLite generated columns yazısına bakın.
REINDEX komutu
REINDEX, indeksleri silip sıfırdan yeniden oluşturur. Bir karşılaştırma kuralının (collation) tanımı değiştiğinde veya ifade indeksinde kullanılan bir fonksiyonun davranışı değiştiğinde kullanılır. Argümansız REINDEX bağlı bütün veritabanlarındaki bütün indeksleri; bir karşılaştırma kuralının adıyla o kuralı kullanan indeksleri; bir tablo adıyla o tablonun indekslerini; bir indeks adıyla yalnızca o indeksi yeniden oluşturur:
REINDEX belge2_tur; -- tek bir indeks
REINDEX belge2; -- bir tablonun bütün indeksleri
REINDEX BINARY; -- BINARY karşılaştırma kuralını kullanan bütün indeksler
REINDEX; -- bağlı bütün veritabanlarındaki bütün indeksler
REINDEX olmayan;
-- hata: unable to identify the object to be reindexedREINDEX ad biçiminde ad hem bir karşılaştırma kuralına hem de bir tablo veya indekse uyuyorsa, karşılaştırma kuralı önceliklidir. Belgeler bu belirsizliği önlemek için tablo veya indeksi yeniden oluştururken şema adını da yazmayı önerir; örneğin REINDEX main.belge2.
SQLite 3.53.0 ile REINDEX'e EXPRESSIONS argümanı eklendi. Bu biçim yalnızca ifade indekslerini yeniden oluşturur, diğer indekslere dokunmaz; veritabanında ifade indeksi yoksa hiçbir şey yapmaz. Komut satırı aracının 3.53.0 ve sonraki sürümlerinde .indexes --expr komutu da hangi indekslerin ifade indeksi olduğunu listeler. Bu iki komut deneme araçlarımızdan yeni olduğu için örneği yalnızca belgeye dayanarak veriyoruz:
-- Yalnızca SQLite 3.53.0 ve sonrası (bu yazının deneme araçlarında çalıştırılmadı)
REINDEX EXPRESSIONS;
-- Komut satırı aracında, 3.53.0 ve sonrası: ifade indekslerini listeler
.indexes --exprBelgeler bir ayrıntıyı daha not eder: ad EXPRESSIONS olduğunda komut, ifade indekslerine ek olarak "expressions" adlı bir karşılaştırma kuralını kullanan indeksleri, "expressions" adlı bir tablonun indekslerini veya bu adı taşıyan bir indeksi de öncelik kurallarına göre yeniden oluşturur. SQLite 3.53 ile gelen diğer değişiklikler için SQLite 3.53 yenilikleri yazısına bakın.
Şema değişikliğinde kısmi ve ifade indeksleri
Bir sütun kısmi indeksin WHERE koşulunda geçiyorsa veya indekslenmişse ALTER TABLE DROP COLUMN başarısız olur. Önce indeksi silmeniz, sütunu kaldırmanız ve gerekiyorsa indeksi yeni biçimiyle yeniden oluşturmanız gerekir:
ALTER TABLE t2 DROP COLUMN c;
-- hata: error in index t2_bc after drop column: no such column: cSürüm uyumluluğu da göz önünde tutulmalıdır. Kısmi indeksler 3.8.0, ifade indeksleri 3.9.0 sürümünden beri desteklenir. Bu tür bir indeks içeren veritabanı daha eski sürümlerle açılamaz; belgelere göre kısmi indeksleri DROP INDEX ile silmek dosyayı eski sürümler için yeniden okunur hâle getirir. Şema değişikliklerini güvenle uygulamak için ALTER TABLE ve şema göçü yazılarına bakın.
Sık yapılan hatalar

- Sorgudaki koşulu indeks tanımından farklı yazmak;
durum = 'acik'ile kurulan indeks başka bir yazımla eşleşmez. - Kısmi indeksin koşulundaki değeri sorguya parametreyle vermek ve indeksin kullanılmadığını fark etmemek.
- İndeks koşulunu
6=bgibi sabit solda olacak biçimde yazmak. - İfade indeksini
->>ile kurup sorgudajson_extract()kullanmak. y + xgibi cebirsel olarak eşdeğer ama metin olarak farklı ifadelerle sorgulamak.- İndeks ifadesinde
'now'ile çalışan tarih fonksiyonu kullanmak; hata ilk yazmada çıkar. - Kısmi UNIQUE indeksi UPSERT hedefi yaparken indeksin WHERE koşulunu hedefe eklemeyi unutmak.
Sık Sorulan Sorular
Kısmi indeks yerine tam indeks kullansam ne kaybederim?
Sorgu sonuçları aynı kalır. Kaybettiğiniz şey alan ve yazma hızıdır: tam indeks, koşulu sağlamayan satırları da içerir ve bu satırlar her eklenip güncellendiğinde indeks de güncellenir. Kısmi UNIQUE indeksin sağladığı alt küme benzersizliğini ise tam indeksle kuramazsınız.
Bir sorgunun ifade indeksini kullanıp kullanmadığını nasıl anlarım?
EXPLAIN QUERY PLAN çıktısında indeks adını ve parantez içinde <expr> ifadesini görürsünüz. Çıktıda yalnızca SCAN varsa sorgudaki ifade indeks tanımıyla eşleşmiyordur.
Bayat ifade indeksi için düzenli REINDEX çalıştırmalı mıyım?
Belgeler ortalama bir geliştirici için bu sorunun pratikte gündeme gelmemesi gerektiğini belirtir. Veritabanını farklı bir mimariye taşıdıysanız, ICU kütüphanesi güncellendiyse veya indekste kullanılan kendi fonksiyonunuzu değiştirdiyseniz REINDEX çalıştırmak mantıklıdır; 3.53.0 ve sonrasında bunun için REINDEX EXPRESSIONS yeterlidir.
İfade indeksi mi, hesaplanan sütun mu?
İfade küçük ve sorgularda hep aynı biçimde yazılıyorsa ifade indeksi yeterlidir. İfade karmaşıksa, birçok sorguda kullanılıyorsa veya ondalık sayı üretiyorsa STORED hesaplanan sütun ve onun üzerindeki indeks daha öngörülebilir bir seçimdir. VIRTUAL hesaplanan sütun üzerine kurulan indeks ise belgelere göre bir ifade indeksi sayılır.
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 · SQLite index oluşturma · SQLite generated columns · SQLite EXPLAIN QUERY PLAN · SQLite JSON fonksiyonları · SQLite 3.53 yenilikleri