Menu

SQLite Type Affinity: Sütun Tipleri Nasıl Çalışır?

SQLite'ın type affinity sistemi nasıl çalışır? Beş affinity türü, CREATE TABLE'da yazdığınız tipten hangisinin seçildiği ve neden bir INTEGER sütunun string tutabildiği.

Bu sayfada çalıştırılabilir editörler var — düzenle, çalıştır ve sonucu anında gör.

Affinity Bir Kural Değil, Tercihtir

SQLite dinamik tiplemeli bir veritabanıdır. Her değerin kendi depolama sınıfı vardır (NULL, INTEGER, REAL, TEXT, BLOB) ve bir sütunun tanımlı tipi, o sütuna ne yazabileceğinizi katı biçimde sınırlamaz. Tanımlı tipin asıl işlevi, sütuna bir affinity (yatkınlık) kazandırmaktır — yani SQLite'ın gelen değerleri dönüştürmeye çalışacağı tercih edilen depolama sınıfı.

Affinity'nin tip uyuşmazlığını engellemeye yetmediği durumlarda neler olduğuna bakalım:

İkinci satır, 'two' string değerini INTEGER tipindeki bir sütunda tutuyor. SQLite önce 'two' ifadesini sayıya çevirmeyi denedi, beceremedi (zaten sayısal değil), sonuçta değeri olduğu gibi TEXT olarak sakladı. typeof() fonksiyonu, her değerin gerçek depolama sınıfını (storage class) gösterir — ve bu, sütunun tanımındaki tiple her zaman örtüşmeyebilir.

Postgres ya da MySQL'den gelenler bu duruma şaşırıyor. Ama bu bir hata değil, tasarımın kendisi böyle.

Beş Type Affinity

STRICT olmayan bir tablodaki her sütun, aşağıdaki affinity'lerden tam olarak birine sahip olur:

  • TEXT — string'leri tercih eder.
  • NUMERIC — sayıları tercih eder, ama dönüştüremezse metni de kabul eder.
  • INTEGERNUMERIC ile benzer çalışır; ondalık kısmı olmayan değerleri integer olarak saklar.
  • REAL — kayan noktalı sayıları tercih eder.
  • BLOB — tercihi yoktur, ne verirseniz onu saklar.

BLOB affinity'sine "no affinity" de denir — sütuna hiçbir tip belirtmediğinizde bu varsayılan olarak gelir.

Aynı girdi — '42' string'i — ama beş farklı saklanmış tip. Her sütun, kendi affinity'sine göre ya dönüştürdü ya da olduğu gibi bıraktı.

SQLite, tanımınızdan affinity'yi nasıl seçer?

İşte insanların kafasını karıştıran kısım: SQLite'ın sabit bir "geçerli tipler" listesi yok. Sütun adından sonra neredeyse istediğiniz şeyi yazabilirsiniz; SQLite, affinity'yi belirlemek için yazdığınız metni şu sırayla tarar ve içinde geçen alt dizilere bakar:

  1. INT içeriyorsa → INTEGER
  2. CHAR, CLOB veya TEXT içeriyorsa → TEXT
  3. BLOB içeriyorsa ya da hiçbir tip belirtilmemişse → BLOB
  4. REAL, FLOA veya DOUB içeriyorsa → REAL
  5. Bunların hiçbiri değilse → NUMERIC

Algoritmanın tamamı bu kadar. Ve aslında bir sürü garipliği de açıklıyor:

FLOATING_POINTS ifadesi INTEGER olarak ele alınır çünkü POINTS kelimesinin içinde INT alt dizisi geçiyor. Eşleşen ilk kural kazanır, yukarıdan aşağıya doğru. Başka bir veritabanından tip tanımlarını körü körüne kopyalamanın neden beklediğinizden farklı sonuçlar doğurabileceğinin sebebi de tam olarak budur.

Affinity İş Başında: Insert Sırasındaki Dönüşümler

Type affinity asıl önemini, SQLite değerinizi dönüştürecek mi yoksa olduğu gibi mi saklayacak diye karar verirken gösterir. Kurallar şöyle:

  • TEXT affinity: sayılar ve BLOB'lar metne dönüştürülür.
  • NUMERIC, INTEGER, REAL affinity: sayıya benzeyen metinler dönüştürülür; benzemeyenler metin olarak kalır.
  • BLOB affinity: hiçbir şey dönüştürülmez.

Satır satır bakalım:

  • NUMERIC sütundaki '123' değeri integer 123 olur. Metinden sayıya dönüşüm başarılı ve veri kaybı olmadan tamamlandı.
  • '12.5' değeri real tipinde 12.5 olur.
  • NUMERIC sütunundaki 'hello' metin olarak kalır — sayıya dönüştürülecek bir şey yok çünkü.
  • TEXT sütunu sayıları string formuna çevirir.
  • BLOB sütunu ise her şeyi olduğu gibi, tipiyle birlikte saklar.

INTEGER ile REAL Arasındaki İncelik

INTEGER affinity, NUMERIC'e neredeyse birebir benzer şekilde çalışır; tek bir farkla: 3.0 gibi ondalık kısmı sıfır olan bir değer, yerden tasarruf etmek için integer 3 olarak saklanır.

3.0 her iki sütunda da INTEGER olarak yer alır — bu optimizasyon NUMERIC için de geçerli. 3.5 ise ondalık kısmını koruyup REAL olarak kalır. Buradan çıkaracağımız ders: bir sütunun INTEGER mi yoksa REAL mi olarak tanımlandığını anlamak için typeof() fonksiyonuna güvenmeyin. Bu fonksiyon size sütunun nasıl tanımlandığını değil, o satırda fiilen ne saklandığını söyler — ve bu değer satırdan satıra değişebilir.

SQLite Type Affinity Sizi Ne Zaman Yakar?

Bu esneklik, işler ters gidene kadar pek hoştur. Gerçek projelerde sıkça karşılaşılan iki tuzak var:

1. Hatalı veri sızıntısı. Uygulamanızda bir bug yüzünden INTEGER sütununa 'N/A' gönderiliyorsa, SQLite bunu hiç itiraz etmeden kaydeder. Sonradan o sütun üzerinde aritmetik yapan sorgular tuhaf sonuçlar veya NULL döndürür. Hata yok, uyarı yok — sadece sessiz sedasız bozulan veri.

2. Karşılaştırmalar tuhaflaşır. Sıralama ve eşitlik kontrolleri, farklı depolama sınıflarındaki değerlere farklı davranır:

Sayılar önce sayısal olarak, ardından metin değerleri sözlük sırasına göre sıralanır — üstelik metinler tüm sayılardan sonra gelir. Yani sonuç olarak önce 2, 3, 10 (sayısal sırayla tam sayılar), sonra '20', '100' (alfabetik sırayla string'ler) elde edersiniz. Çoğu insanın beklediği bu değildir.

Eğer veriyi kendiniz ekliyor ve titizce doğruluyorsanız, normal tablolar iş görür. Ama bu kontrol sizde değilse — ya da tip denetimini doğrudan veritabanına bırakmak istiyorsanız — daha iyi bir yol var.

Sırada: STRICT Tablolar

SQLite 3.37 ile gelen STRICT tablolar, affinity davranışını devre dışı bırakır ve bildirilen tiple uyuşmayan değerleri reddeder. İstediğinizde varsayılan dinamik tiplemeyi, istemediğinizde ise Postgres tarzı sıkı denetimi sağlar. Bir sonraki sayfada bunu işliyoruz.

Sıkça Sorulan Sorular

SQLite'ta type affinity nedir?

Type affinity, bir sütunun tercih ettiği depolama sınıfıdır. SQLite'ta beş tane vardır: TEXT, NUMERIC, INTEGER, REAL ve BLOB. Bir değer eklediğinizde SQLite önce onu sütunun affinity'sine çevirmeyi dener; ama bu çevrim veri kaybına yol açacaksa ya da imkânsızsa değeri olduğu gibi saklar. Yani affinity katı bir kural değil, bir öneridir.

SQLite bir sütunun affinity'sini nasıl belirliyor?

SQLite, CREATE TABLE içinde yazdığınız tip adını sırayla belirli alt dizeler için tarar: içinde INT geçiyorsa INTEGER olur; geçmiyorsa CHAR, CLOB veya TEXT varsa TEXT olur; yoksa BLOB (ya da hiç tip yazmadıysanız) BLOB olur; sonra REAL, FLOA veya DOUB varsa REAL olur; hiçbiri tutmuyorsa NUMERIC olur. Bu yüzden VARCHAR(50) TEXT'e, BIGINT ise INTEGER'a düşer — yazdığınız kelimeler basit bir pattern matching'ten geçer.

SQLite sütununa yanlış tipte değer yazılabilir mi?

Normal tablolarda evet. INTEGER olarak tanımlanmış bir sütun, 'hello' string'ini hiç sorun çıkarmadan saklar; çünkü affinity yalnızca bir çevrim önerisi sunar. Tip kontrolünü gerçekten zorunlu kılmak istiyorsanız STRICT tabloları kullanmanız gerekir; bunlar uyumsuz değerleri doğrudan reddeder. Bir sonraki yazıda bunu ele alıyoruz.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA