SQLite'ta bir tablodan veri okumak için SELECT sütunlar FROM tablo WHERE koşul ORDER BY sıralama LIMIT n kalıbını kullanırsınız: WHERE satırları süzer, ORDER BY sonucu sıralar, LIMIT ve OFFSET ise kaç satırın, hangi noktadan başlayarak döneceğini belirler. Sıralama belirtmediğiniz sürece satırların hangi sırayla geleceği tanımsızdır; bu yüzden sayfalama veya "ilk N kayıt" gibi işlerde ORDER BY yazmak zorunludur.
Bu yazıda SELECT ifadesinin çekirdeğini SQLite belgelerinde anlatılan işlem sırasıyla ele alıyoruz: satırların nereden geldiği, süzmenin NULL değerlerle nasıl çalıştığı, sıralamada NULL'ların yeri, LIMIT ifadesinin az bilinen kuralları, DISTINCT ve UNION, INTERSECT, EXCEPT ile birleşik sorgular. Tabloları birleştirme konusu SQLite JOIN türleri, gruplama ve özet fonksiyonları ise GROUP BY ve aggregate fonksiyonlar yazısında ayrıca işleniyor.
Örnek tablo

Aşağıdaki örneklerin hepsi aynı küçük ürün tablosunu kullanıyor. Kodu sqlite3 istemine yapıştırarak kendiniz deneyebilirsiniz; komut satırı aracı dosya adı verilmeden açıldığında bellekte geçici bir veritabanı kullanır, yani denemeleriniz hiçbir dosyayı değiştirmez. Antivirus satırında stok bilgisini bilerek NULL bıraktık; NULL'ın süzme ve sıralamadaki etkisini bu satır üzerinden göreceğiz.
CREATE TABLE urun(
id INTEGER PRIMARY KEY,
ad TEXT,
kategori TEXT,
fiyat INTEGER,
stok INTEGER
);
INSERT INTO urun(ad, kategori, fiyat, stok) VALUES
('Klavye', 'donanim', 450, 12),
('Fare', 'donanim', 180, 0),
('Monitor', 'donanim', 3200, 5),
('Antivirus', 'yazilim', 300, NULL),
('Ofis paketi', 'yazilim', 1200, 40),
('USB kablo', 'aksesuar', 60, 150);SELECT ifadesi hangi sırayla işlenir?
SQLite belgeleri basit bir SELECT ifadesinin sonucunu dört adımlık bir süreç olarak tarif eder. Önce FROM bölümü işlenir ve sorgunun üzerinde çalışacağı girdi satırları belirlenir. Ardından WHERE ifadesi her satır için hesaplanır ve yalnızca sonucu doğru olan satırlar kalır. Üçüncü adımda, sorguda gruplama varsa satırlar gruplanır ve SELECT ile FROM arasındaki sonuç ifadeleri hesaplanır. Son adımda DISTINCT yazılmışsa yinelenen satırlar atılır. Sıralama ve satır sınırı bu sürecin sonunda, sonuç kümesinin tamamına uygulanır.
Bu sıra, sık karşılaşılan birkaç davranışı açıklar. WHERE sonuç ifadelerinden önce çalıştığı için bir özet fonksiyonunu WHERE içinde kullanamazsınız; bunun yeri HAVING bölümüdür. Sıralama ise sonuç ifadeleri hesaplandıktan sonra yapıldığından ORDER BY içinde sütun takma adlarını ve sütun numaralarını kullanabilirsiniz. Girdi tarafında FROM hiç yazılmazsa SQLite sorguyu sıfır sütunlu tek bir satır üzerinde çalıştırır; bu sayede SELECT bir hesap makinesi gibi de kullanılabilir.
SELECT 2 + 3, 'SQL' || 'ite', typeof(1.0);
-- çıktı: 5|SQLite|realBurada typeof() fonksiyonu değerin depolama sınıfını döndürüyor. 1.0 yazdığınızda değer ondalık (real) olarak saklanır; tamsayı ile ondalık arasındaki bu fark, sıralama ve karşılaştırmada önemli hâle gelir. Tip sisteminin ayrıntıları SQLite veri tipleri ve type affinity yazısında.
WHERE ile süzme
WHERE ifadesi girdi satırlarının her biri için mantıksal bir değer olarak hesaplanır. Belgelerin vurguladığı kural şudur: sonucu yanlış olan satırlar da, sonucu NULL olan satırlar da elenir. Satırın sonuca girmesi için ifadenin kesin olarak doğru olması gerekir. Karşılaştırma, AND, OR, BETWEEN ve IN gibi tanıdık işleçlerin hepsi burada kullanılabilir.
SELECT ad, fiyat
FROM urun
WHERE kategori = 'donanim' AND fiyat < 1000
ORDER BY fiyat DESC;
-- çıktı:
-- Klavye|450
-- Fare|180
SELECT ad, fiyat FROM urun WHERE fiyat BETWEEN 300 AND 1200 ORDER BY fiyat;
-- çıktı:
-- Antivirus|300
-- Klavye|450
-- Ofis paketi|1200
SELECT ad FROM urun WHERE kategori IN ('yazilim', 'aksesuar') ORDER BY ad;
-- çıktı:
-- Antivirus
-- Ofis paketi
-- USB kabloBETWEEN iki ucu da dahil eden bir aralıktır; x BETWEEN 300 AND 1200 yazmak x >= 300 AND x <= 1200 yazmakla aynı sonucu verir, tek farkı x ifadesinin yalnızca bir kez hesaplanmasıdır. IN ise sağındaki listede veya alt sorguda sol taraftaki değerin bulunup bulunmadığını sorar.
NULL değerler WHERE'de neden kaybolur?
Stok sütunu NULL olan Antivirus satırı, aşağıdaki iki sorgunun hiçbirinde görünmez. stok > 0 ifadesi bu satır için NULL üretir, NOT (stok > 0) da NULL üretir; ikisi de "doğru" olmadığından satır her iki durumda da elenir. İlk bakışta bir koşul ile onun tersinin toplamının bütün tabloyu vermesi beklenir, ancak NULL içeren sütunlarda bu beklenti tutmaz.
SELECT ad FROM urun WHERE stok > 0;
-- çıktı:
-- Klavye
-- Monitor
-- Ofis paketi
-- USB kablo
SELECT ad FROM urun WHERE NOT (stok > 0);
-- çıktı: Fare
SELECT ad FROM urun WHERE stok = NULL;
-- (hiç satır dönmez)
SELECT ad FROM urun WHERE stok IS NULL;
-- çıktı: Antivirusstok = NULL karşılaştırması da her satır için NULL üretir ve hiçbir satır dönmez. NULL olan satırları bulmak için IS NULL, NULL olmayanlar için IS NOT NULL kullanmanız gerekir. NULL'ın karşılaştırmalardaki, IN listelerindeki ve özet fonksiyonlarındaki davranışı ayrı bir yazı gerektirecek kadar geniş; ayrıntılar için SQLite NULL davranışı yazısına bakın.
ORDER BY ile sıralama

Birden fazla satır döndüren bir sorguda ORDER BY yoksa SQLite satırları herhangi bir sırada verebilir. Bugün tablonun eklenme sırasıyla geliyor gibi görünen bir sonuç, bir indeks eklendiğinde veya sorgu planı değiştiğinde başka bir sıraya geçebilir. Sıranın önemli olduğu her yerde sıralamayı açıkça yazın.
Sıralama soldan sağa yapılır: önce ilk ifadeye göre sıralanır, eşitlikler ikinci ifadeyle çözülür ve bu böyle devam eder. Bütün ifadeleri eşit olan iki satırın kendi arasındaki sırası yine tanımsızdır; bu nedenle sayfalama yaptığınız sorgularda sıralamanın sonuna benzersiz bir sütun (örneğin id) eklemek iyi bir alışkanlıktır. ASC küçükten büyüğe, DESC büyükten küçüğe sıralar; hiçbiri yazılmazsa artan sıra kullanılır.
SELECT ad, stok FROM urun ORDER BY stok;
-- çıktı:
-- Antivirus|
-- Fare|0
-- Monitor|5
-- Klavye|12
-- Ofis paketi|40
-- USB kablo|150
SELECT ad, stok FROM urun ORDER BY stok NULLS LAST;
-- çıktı:
-- Fare|0
-- Monitor|5
-- Klavye|12
-- Ofis paketi|40
-- USB kablo|150
-- Antivirus|
SELECT kategori, ad FROM urun ORDER BY kategori, fiyat DESC;
-- çıktı:
-- aksesuar|USB kablo
-- donanim|Monitor
-- donanim|Klavye
-- donanim|Fare
-- yazilim|Ofis paketi
-- yazilim|Antivirus
SELECT ad, fiyat * stok AS deger FROM urun ORDER BY deger DESC LIMIT 2;
-- çıktı:
-- Ofis paketi|48000
-- Monitor|16000SQLite sıralama sırasında NULL'ı bütün değerlerden küçük kabul eder. Bu yüzden artan sıralamada NULL'lar başa, azalan sıralamada sona düşer. Bu davranışı NULLS FIRST ve NULLS LAST ile değiştirebilirsiniz; bu sözdizimi SQLite 3.30.0 sürüm notu ile eklendi. Daha eski bir SQLite kullanıyorsanız aynı etkiyi ORDER BY stok IS NULL, stok gibi bir ifadeyle elde edebilirsiniz, çünkü IS NULL 0 veya 1 döndürür.
ORDER BY içindeki her terim üç kurala göre yorumlanır. Terim sabit bir tamsayıysa sonuç kümesindeki o sıradaki sütunu ifade eder; ORDER BY 1 ilk sütuna göre sıralar. Terim sonuç sütunlarından birinin takma adıysa o sütunu ifade eder; yukarıdaki deger örneği böyle çalışıyor. Bunların dışında kalan her ifade yeniden hesaplanır ve değeri sıralama anahtarı olur. Metinleri karşılaştırırken kullanılan harmanlama (collation) varsayılan olarak sütunun kendi harmanlamasıdır, o da belirtilmemişse BINARY'dir; yani bayt bayt karşılaştırma yapılır ve Türkçe harfler İngilizce alfabenin arkasına düşer. Bu konu COLLATE ve NOCASE yazısında ayrıntılı olarak anlatılıyor.
LIMIT ve OFFSET kuralları
LIMIT bütün SELECT ifadesinin döndüreceği satır sayısına üst sınır koyar. OFFSET eklenirse önce o kadar satır atlanır, ardından sınır kadar satır döner. Sorgu zaten sınırdan az satır üretiyorsa hepsi döner; bu bir hata değildir.
SELECT ad FROM urun ORDER BY id LIMIT 2 OFFSET 2;
-- çıktı:
-- Monitor
-- Antivirus
-- Virgüllü biçim: ilk sayı OFFSET, ikinci sayı LIMIT
SELECT ad FROM urun ORDER BY id LIMIT 2, 3;
-- çıktı:
-- Monitor
-- Antivirus
-- Ofis paketi
SELECT count(*) FROM (SELECT ad FROM urun LIMIT -1);
-- çıktı: 6
SELECT ad FROM urun ORDER BY id LIMIT 2 OFFSET -5;
-- çıktı:
-- Klavye
-- Fare
SELECT ad FROM urun ORDER BY id LIMIT NULL;
-- hata: datatype mismatchBu örnekte belgelerde yazan ama sık gözden kaçan dört kural görünüyor. Birincisi, LIMIT 2, 3 biçiminde virgülle iki sayı yazdığınızda ilk sayı atlanacak satır sayısı, ikinci sayı sınırdır; yani bu ifade LIMIT 3 OFFSET 2 ile aynıdır. Sıranın OFFSET sözcüğüyle yazılan biçimin tersi olması bilinçli bir uyumluluk tercihidir ve belgeler karışıklığı önlemek için OFFSET sözcüklü biçimi önerir. İkincisi, negatif bir LIMIT sınırı tamamen kaldırır. Üçüncüsü, negatif bir OFFSET sıfır gibi davranır. Dördüncüsü, LIMIT ifadesi NULL'a veya kayıpsız biçimde tamsayıya çevrilemeyen bir değere dönüşürse sorgu hata verir.
LIMIT içine sabit sayı yazmak zorunda değilsiniz; tamsayıya dönüşebilen her skaler ifade kabul edilir. Uygulamanızdan sayfa numarası alıyorsanız değerleri sorgu metnine eklemek yerine parametre olarak bağlayın. Parametreli sorguların neden önemli olduğu SQL injection önleme yazısında anlatılıyor. Bu yaklaşımda boş bırakılan bir parametrenin NULL sayıldığını, NULL bir LIMIT değerinin ise yukarıdaki hatayı verdiğini unutmayın.
DISTINCT ile yinelenenleri atmak
SELECT DISTINCT sonuç kümesindeki tamamen aynı satırları teke indirir. ALL yazmak veya hiçbir şey yazmamak bütün satırları döndürür. Yinelenen satırları belirlerken SQLite değerleri IS DISTINCT FROM mantığıyla karşılaştırır; bunun üç pratik sonucu vardır. İki NULL birbirine eşit sayılır ve sonuçta tek bir NULL kalır. Aynı miktarı gösteren tamsayı ile ondalık sayı (1 ve 1.0) eşit kabul edilir. Buna karşılık karşılaştırmada tip dönüşümü yapılmaz, bu yüzden metin olan '1' sayı olan 1 ile aynı sayılmaz.
SELECT DISTINCT kategori FROM urun ORDER BY kategori;
-- çıktı:
-- aksesuar
-- donanim
-- yazilim
SELECT count(*) FROM (
SELECT DISTINCT column1 FROM (VALUES (1), (1.0), (NULL), (NULL), ('1'))
);
-- çıktı: 3İkinci sorgudaki beş değer bu kurallar yüzünden üç farklı değere iner: 1 (1.0 ile birleşir), NULL (iki NULL birleşir) ve metin '1'. DISTINCT bütün sütunlara birlikte bakar; tek bir sütunda yinelenenleri atıp diğer sütunlardan bir değer seçmek istiyorsanız aradığınız şey genellikle gruplamadır.
UNION, INTERSECT ve EXCEPT

Birleşik (compound) sorgu, iki veya daha fazla basit SELECT ifadesini bir küme işleciyle bağlar. UNION ALL iki tarafın bütün satırlarını art arda verir. UNION aynı işi yapar ama yinelenen satırları atar. INTERSECT iki tarafta da bulunan satırları, EXCEPT ise sol tarafta olup sağ tarafta olmayan satırları döndürür; bu iki işleç de sonuçtaki yinelenenleri kaldırır.
CREATE TABLE eski_musteri(eposta TEXT);
CREATE TABLE yeni_musteri(eposta TEXT);
INSERT INTO eski_musteri VALUES ('ali@ornek.com'), ('ayse@ornek.com'), ('can@ornek.com');
INSERT INTO yeni_musteri VALUES ('ayse@ornek.com'), ('deniz@ornek.com'), ('can@ornek.com');
SELECT eposta FROM eski_musteri
UNION
SELECT eposta FROM yeni_musteri
ORDER BY 1;
-- çıktı:
-- ali@ornek.com
-- ayse@ornek.com
-- can@ornek.com
-- deniz@ornek.com
SELECT count(*) FROM (
SELECT eposta FROM eski_musteri UNION ALL SELECT eposta FROM yeni_musteri
);
-- çıktı: 6
SELECT eposta FROM eski_musteri INTERSECT SELECT eposta FROM yeni_musteri ORDER BY eposta;
-- çıktı:
-- ayse@ornek.com
-- can@ornek.com
SELECT eposta FROM eski_musteri EXCEPT SELECT eposta FROM yeni_musteri;
-- çıktı: ali@ornek.com| İşleç | Döndürdüğü | Yinelenenler |
|---|---|---|
| UNION ALL | İki tarafın bütün satırları | Korunur |
| UNION | İki tarafın bütün satırları | Atılır |
| INTERSECT | İki tarafta da olan satırlar | Atılır |
| EXCEPT | Solda olup sağda olmayan satırlar | Atılır |
Yinelenen kontrolüne ihtiyacınız yoksa UNION ALL tercih edin; UNION yinelenenleri bulabilmek için daha önce gördüğü satırları akılda tutmak zorundadır. Birleşik sorgularda NULL değerler birbirine eşit, NULL olmayan her değerden farklı kabul edilir ve karşılaştırmada tip dönüşümü uygulanmaz.
Birleşik sorgularda ORDER BY ve LIMIT
Birleşik sorguyu oluşturan parçalar kendi başlarına ORDER BY veya LIMIT içeremez. Sıralama ve sınır yalnızca en sondaki parçaya yazılır ve birleşik sorgunun tamamına uygulanır. Ayrıca bütün parçaların aynı sayıda sütun döndürmesi gerekir. Bu kurallardan birini ihlal ettiğinizde SQLite sorguyu hiç çalıştırmadan reddeder:
SELECT eposta FROM eski_musteri ORDER BY eposta
UNION
SELECT eposta FROM yeni_musteri;
-- hata: ORDER BY clause should come after UNION not before
SELECT eposta, 'eski' FROM eski_musteri UNION SELECT eposta FROM yeni_musteri;
-- hata: SELECTs to the left and right of UNION do not have the same number of result columnsBirleşik sorgunun sonundaki ORDER BY terimleri her zaman sonuç sütunlarının takma adı olarak yorumlanır. Bir sütunu sıra numarasıyla (ORDER BY 1) ya da sol parçadaki adıyla belirtebilirsiniz; sonuç sütunlarından birine karşılık gelmeyen bir ifade yazarsanız hata alırsınız. Tek bir parçayı ayrıca sıralamanız gerekiyorsa o parçayı parantez içinde bir alt sorgu olarak FROM bölümüne taşıyın.
Üç veya daha fazla parça varsa işlemler soldan sağa gruplanır: A UNION B EXCEPT C, önce A ile B birleştirilip ardından C çıkarılarak hesaplanır.
VALUES ve alt sorgular
VALUES tek başına bir sorgu olarak da kullanılabilir. VALUES(a, b) yazmak SELECT a, b yazmakla aynıdır; virgülle ayrılmış birden fazla satır ise parçaları UNION ALL ile bağlanmış bir birleşik sorgu gibi davranır. Sütunlar otomatik olarak column1, column2 diye adlandırılır. Belgelerde belirtilen iki kısıt vardır: bir VALUES ifadesinin arkasına doğrudan ORDER BY veya LIMIT yazılamaz. Sıralama gerekiyorsa yukarıdaki gibi VALUES ifadesini bir alt sorguya koyup dışarıda sıralayın.
VALUES (1, 'bir'), (2, 'iki');
-- çıktı:
-- 1|bir
-- 2|iki
SELECT * FROM (VALUES (1, 'bir'), (2, 'iki')) WHERE column1 > 1;
-- çıktı: 2|iki
SELECT ad, fiyat FROM urun WHERE fiyat > (SELECT avg(fiyat) FROM urun);
-- çıktı:
-- Monitor|3200
-- Ofis paketi|1200FROM bölümündeki bir alt sorgu, sonucunu içeren bir tabloymuş gibi işlenir ve sütunları alt sorgudaki ifadelerin harmanlama ve tip eğilimini taşır. Son sorgudaki parantez içindeki SELECT avg(fiyat) FROM urun ise skaler bir alt sorgudur: ilk satırının ilk sütununu döndürür, hiç satır üretmezse değeri NULL olur. Aynı alt sorguyu birden çok yerde kullanacaksanız onu bir ortak tablo ifadesi (CTE) olarak tanımlamak okunabilirliği artırır; bkz. SQLite WITH ve recursive CTE.
Sık yapılan hatalar
- Sıralama olmadan sıra beklemek:
ORDER BYyoksa satır sırası tanımsızdır. Bugün doğru görünen sıra, şema veya veri değiştiğinde bozulabilir. - NULL'ı
=ile aramak:sutun = NULLhiçbir zaman doğru olmaz;IS NULLkullanın. - Bir koşul ile tersinin bütün satırları kapsadığını varsaymak: NULL içeren bir sütunda
x > 0ileNOT (x > 0)birlikte bile NULL satırları getirmez. - Virgüllü
LIMITbiçimini ters okumak:LIMIT 10, 20on satır atlayıp yirmi satır döndürür. Karışıklığı önlemek içinLIMIT 20 OFFSET 10yazın. - Birleşik sorgunun ortasına
ORDER BYkoymak: sıralama yalnızca en sonda yer alabilir ve bütün sonuca uygulanır. - Özet fonksiyonunu
WHEREiçinde kullanmak:WHERE sum(x) > 10hata verir; bu koşulHAVINGbölümüne yazılır.
Sorgunuz doğru sonucu verdiği hâlde yavaşsa, SQLite'ın onu nasıl çalıştırdığını görmek için EXPLAIN QUERY PLAN yazısına, sık kullandığınız WHERE ve ORDER BY sütunları için indeks oluşturmak için de SQLite index oluşturma yazısına bakabilirsiniz.
Sık Sorulan Sorular
SELECT * kullanmak sakıncalı mı?
* girdi verisinin bütün sütunlarını sonuca koyar; tablo.* ise yalnızca o tablonun sütunlarını. Komut satırında hızlı bakış için pratiktir. Uygulama kodunda ise ihtiyacınız olan sütunları adıyla yazmak, tabloya yeni sütun eklendiğinde sonucun şeklinin değişmesini önler. Belgelere göre * yalnızca sonuç ifadesi listesinde kullanılabilir ve FROM bölümü olmayan bir sorguda kullanılamaz.
Sayfalama için OFFSET yeterli mi?
Küçük tablolarda ORDER BY ... LIMIT n OFFSET m yeterince iş görür. Önemli olan, sıralamanın kararlı olmasıdır: sıralama sütunlarında eşitlik varsa sonuna benzersiz bir sütun ekleyin. Büyük tablolarda yavaşlık fark ederseniz sorgu planını inceleyip sıralama sütunlarına uygun bir indeks olup olmadığını kontrol edin.
NULLS FIRST ve NULLS LAST her sürümde çalışır mı?
Hayır. Bu sözdizimi SQLite 3.30.0 ile geldi. Uygulamanızın kullandığı kütüphane daha eskiyse ORDER BY sutun IS NULL, sutun ifadesi aynı sonucu verir. Sürümünüzü SELECT sqlite_version(); ile öğrenebilirsiniz.
UNION ile UNION ALL arasında hangisini seçmeliyim?
İki kümede ortak satırların teke inmesini istiyorsanız UNION, istemiyorsanız ya da kümelerin zaten çakışmadığını biliyorsanız UNION ALL kullanın. UNION ALL yinelenen kontrolü yapmadığı için daha az iş yapar.
Sonuçları tablo veya CSV olarak nasıl görürüm?
Bu yazıdaki çıktılar komut satırının list biçiminde, sütunlar | ile ayrılmış olarak verildi. Başlık satırı, kutulu tablo, CSV veya JSON çıktısı için SQLite .mode çıktı biçimleri yazısına bakın.
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 JOIN türleri · GROUP BY ve aggregate fonksiyonlar · SQLite NULL davranışı · WITH ve recursive CTE · SQL nedir