İçeriğe geç
academia.sh

Ders 05 / 18

Boş Değerlerle Çalışma

Üç değerli mantığın sorgudaki sonuçları, boş değer sınamaları, boş değer yerine geçen işlevler, koşullu ifade ile boşluk doldurma ve liste koşulunun değillemesindeki tuzak.

İçindekiler

Önceki üç ders boş değerle üç kez karşılaştı: hesaplanmış bir ifadeyi boşalttı, bir koşulun değillemesini eksik bıraktı, sıralamada motora bağlı bir yer aldı. Her seferinde açıklama ertelendi. Bu ders ertelenen açıklamayı yapar.

Boş değer (null) bir değer değil, değerin yokluğunu bildiren bir işarettir. Sıfır değildir, boş dize değildir; “bu satır için bu bilgi yok” demektir. İlişkisel Kuram kursunda boş değerin şemadaki anlamı tanımlanmıştı. Burada sorgudaki davranışı kurulur ve o davranış tek bir kaynaktan çıkar: bilinmeyen bir değerle yapılan karşılaştırmanın sonucu da bilinmezdir.

Aşağıdaki blok şemayı ve örnek veriyi kurar; dersin bütün sorguları bu blokta oluşturulan kutuphane.db dosyası üzerinde çalışır.

rm -f kutuphane.db
sqlite3 kutuphane.db <<'SQL'
CREATE TABLE sube (sube_id INTEGER PRIMARY KEY, ad TEXT NOT NULL, sehir TEXT NOT NULL);
CREATE TABLE kitap (kitap_id INTEGER PRIMARY KEY, baslik TEXT NOT NULL, yazar TEXT NOT NULL,
                    basim_yili INTEGER, sube_id INTEGER REFERENCES sube(sube_id));
CREATE TABLE uye (uye_id INTEGER PRIMARY KEY, ad TEXT NOT NULL, soyad TEXT NOT NULL,
                  eposta TEXT, kayit_tarihi TEXT NOT NULL);
CREATE TABLE odunc (odunc_id INTEGER PRIMARY KEY, kitap_id INTEGER NOT NULL REFERENCES kitap(kitap_id),
                    uye_id INTEGER NOT NULL REFERENCES uye(uye_id), alis_tarihi TEXT NOT NULL,
                    iade_tarihi TEXT);
INSERT INTO sube VALUES (1,'Merkez','Ankara'),(2,'Bahçelievler','Ankara'),
  (3,'Kadıköy','İstanbul'),(4,'Konak','İzmir');
INSERT INTO kitap VALUES (1,'Körlük','José Saramago',1995,1),(2,'Tutunamayanlar','Oğuz Atay',1972,1),
  (3,'Kum Kitabı','Jorge Luis Borges',1975,2),(4,'Yaban','Yakup Kadri',1932,2),
  (5,'Sessiz Ev','Orhan Pamuk',1983,3),(6,'Anayurt Oteli','Yusuf Atılgan',NULL,3),
  (7,'Tehlikeli Oyunlar','Oğuz Atay',1973,NULL);
INSERT INTO uye VALUES (1,'Ayşe','Demir','[email protected]','2023-02-14'),
  (2,'Mehmet','Kaya','[email protected]','2023-05-30'),(3,'Zeynep','Arslan',NULL,'2024-01-09'),
  (4,'Emre','Yıldız','[email protected]','2024-03-22'),(5,'Selin','Aydın',NULL,'2024-11-05'),
  (6,'Burak','Şahin','[email protected]','2025-01-18');
INSERT INTO odunc VALUES (1,1,1,'2025-01-10','2025-01-24'),(2,2,1,'2025-02-02','2025-02-20'),
  (3,1,2,'2025-02-11',NULL),(4,3,3,'2025-03-01','2025-03-15'),(5,4,3,'2025-03-18','2025-04-02'),
  (6,1,4,'2025-04-05','2025-04-19'),(7,5,4,'2025-04-21',NULL),(8,2,5,'2025-05-02','2025-05-30'),
  (9,7,1,'2025-05-14','2025-05-28'),(10,3,5,'2025-06-03',NULL),(11,6,2,'2025-06-11','2025-06-25'),
  (12,4,4,'2025-06-20','2025-07-04');
SQL

Karşılaştırmanın Sonucu

Boş değerin eşitlik sınamasındaki davranışı doğrudan ölçülebilir.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT NULL = NULL AS esit, NULL <> NULL AS farkli, NULL IS NULL AS bos_mu;
SQL
esit  farkli  bos_mu
----  ------  ------
              1     

İlk iki sütun boş; üçüncüsü 1 yani doğru. Bir boş değer başka bir boş değere ne eşittir ne de eşit değildir. İki bilinmeyen sayının birbirine eşit olup olmadığı bilinemez, çünkü her ikisi de bilinmiyor. Eşitlik işleci bu durumda ne doğru ne yanlış üretir; üçüncü bir mantıksal değer üretir: bilinmeyen (unknown).

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT 1 = 1 AS dogru, 1 = 2 AS yanlis, 1 = NULL AS bilinmiyor;
SQL
dogru  yanlis  bilinmiyor
-----  ------  ----------
1      0                 

Üç sütun mantığın üç değerini gösteriyor: doğru 1, yanlış 0, bilinmeyen ise boş. WHERE yan tümcesinin yalnız doğruyu geçirdiği kuralı burada bir sonuca bağlanır: boş değer içeren bir karşılaştırma hiçbir satırı geçirmez — ne koşulda, ne değillemesinde.

Boş değeri sınamanın tek doğru yolu IS NULL ve IS NOT NULL yazımlarıdır. Bunlar karşılaştırma işleci değil, doğrudan boşluk sınayan yüklemlerdir ve her zaman doğru ya da yanlış üretirler.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, basim_yili FROM kitap WHERE basim_yili IS NULL;
SQL
baslik         basim_yili
-------------  ----------
Anayurt Oteli            

Üç Değerli Mantık

Mantıksal bağlaçlar da üç değerle çalışır. Kural, bilinmeyeni “belki doğru belki yanlış” diye okumaktır: sonuç her iki olasılıkta da aynıysa belirlenir, farklıysa bilinmeyen kalır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT 1 AND NULL AS d_ve_b, 0 AND NULL AS y_ve_b, 1 OR NULL AS d_veya_b,
       0 OR NULL AS y_veya_b, NOT NULL AS degil_b;
SQL
d_ve_b  y_ve_b  d_veya_b  y_veya_b  degil_b
------  ------  --------  --------  -------
        0       1                          

İki sütun belirlendi. Yanlış ile bilinmeyenin AND bağlacı yanlıştır: bilinmeyen ne olursa olsun sonuç yanlış. Doğru ile bilinmeyenin OR bağlacı doğrudur: bir taraf zaten doğru. Kalan üçü bilinmeyen kaldı. Değilleme bilinmeyeni değiştirmez — bilinmeyenin tersi de bilinmeyendir.

Bu tablo, Programlama Temelleri kursundaki kısa devre değerlendirme kuralının üç değerli karşılığıdır: bir bağlacın sonucu bir taraftan belirlenebiliyorsa diğer taraf sonucu etkilemez.

Boş Değer Yerine Değer Koymak

Sonuç kümesinde boş yerine anlamlı bir değer göstermek için standart işlev COALESCE kullanılır. Argümanlarını soldan sağa değerlendirir ve boş olmayan ilkini döndürür.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT ad, soyad, COALESCE(eposta,'(adres yok)') AS iletisim FROM uye;
SQL
ad      soyad   iletisim         
------  ------  -----------------
Ayşe    Demir   [email protected]  
Mehmet  Kaya    [email protected]
Zeynep  Arslan  (adres yok)      
Emre    Yıldız  [email protected]  
Selin   Aydın   (adres yok)      
Burak   Şahin   [email protected] 

COALESCE ikiden çok argüman alabilir; öncelikli kaynaklardan ilk dolu olanı seçmek için kullanılır. Birçok motor iki argümanlı bir kısaltma da sunar, ancak o kısaltmanın adı motora göre değişir; taşınabilir yazım COALESCE yazımıdır.

Ters yönde çalışan işlev NULLIF iki argümanı karşılaştırır ve eşitlerse boş değer, değilse birinci argümanı döndürür.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT ad, NULLIF(sehir,'Ankara') AS ankara_disi FROM sube ORDER BY sube_id;
SQL
ad            ankara_disi
------------  -----------
Merkez                   
Bahçelievler             
Kadıköy       İstanbul   
Konak         İzmir      

NULLIF en çok, anlamsız bir yer tutucuyu — sıfır, boş dize, -1 — gerçek boş değere çevirmek için işe yarar. Sıfıra bölmeyi engellemek de yaygın kullanımıdır: bölenin sıfır olduğu durumda boş değer üretmek, hata almaktan daha yönetilebilirdir.

Boş değerin dönüştürülmesi koşullu ifadeyle de yazılabilir. CASE yapısı, bir sütunu okunabilir bir duruma çevirmenin standart yoludur.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT odunc_id, alis_tarihi,
       CASE WHEN iade_tarihi IS NULL THEN 'dışarıda' ELSE 'iade edildi' END AS durum
FROM odunc WHERE odunc_id <= 4 ORDER BY odunc_id;
SQL
odunc_id  alis_tarihi  durum      
--------  -----------  -----------
1         2025-01-10   iade edildi
2         2025-02-02   iade edildi
3         2025-02-11   dışarıda   
4         2025-03-01   iade edildi

CASE WHEN iade_tarihi = NULL yazılsaydı hiçbir satır dışarıda olarak etiketlenmezdi — koşul hiçbir zaman doğru olmazdı ve her satır ELSE dalına düşerdi. Bu, boş değer hatalarının en sinsi biçimidir: sorgu hata vermez, yanlış cevap verir.

Liste Koşulunun Değillemesi

Üç değerli mantığın en pahalı sonucu, liste koşulunun değillemesinde ortaya çıkar. Kitapların bulunduğu şube kimlikleri 1, 2 ve 3; yedinci kitabın şubesi ise henüz belirlenmemiş, yani boş. Hangi şubede hiç kitap olmadığını soran koşul önce boş değer olmadan yazılsın.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT ad FROM sube WHERE sube_id NOT IN (1,2,3);
SQL
ad   
-----
Konak

Doğru yanıt: Konak şubesinde hiç kitap yok. Aynı liste, kitap tablosundaki değerlerden üretilseydi içinde bir de boş değer bulunurdu. Bu durumda ne olduğu ölçülebilir.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT ad FROM sube WHERE sube_id NOT IN (1,2,3,NULL);
SELECT 'dönen satır sayısı: ' || COUNT(*) AS olcum
FROM sube WHERE sube_id NOT IN (1,2,3,NULL);
SQL
olcum                
---------------------
dönen satır sayısı: 0

Birinci sorgu hiçbir satır yazdırmadı; ikincisi bunu sayarak doğruladı. Tek bir boş değer, sorgunun hiçbir satır döndürmemesine yol açtı ve hata verilmedi. Nedeni doğrudan üç değerli mantıktan gelir: NOT IN koşulu, “listedeki her öğeye eşit değil” biçiminde açılır. 4 <> 1 AND 4 <> 2 AND 4 <> 3 AND 4 <> NULL ifadesinin son çarpanı bilinmeyendir; doğru ile bilinmeyenin AND bağlacı da bilinmeyendir. Koşul hiçbir satır için doğru olmaz.

Aynı liste olumlu yönde kullanıldığında sorun görünmez.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT ad FROM sube WHERE sube_id IN (1,2,3,NULL);
SQL
ad          
------------
Merkez      
Bahçelievler
Kadıköy     

IN koşulu OR ile açıldığı için, bir taraf doğru olduğunda bilinmeyen sonucu etkilemez. Tuzak yalnızca değillemededir ve bu asimetri, hatanın gözden kaçmasının başlıca nedenidir: olumlu sorgu doğru çalıştığı için değillemesinin de doğru çalıştığı varsayılır.

Korunma yolu, listeyi üreten kaynakta boş değerleri elemek ya da koşulu boş değere duyarsız bir yüklemle yazmaktır. Standartta bunun için IS DISTINCT FROM yüklemi vardır; boş değerleri de karşılaştırılabilir sayar.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT 1 IS DISTINCT FROM NULL AS bir_ve_bos,
       NULL IS DISTINCT FROM NULL AS bos_ve_bos,
       NULL IS NOT DISTINCT FROM NULL AS bos_bosa_esit;
SQL
bir_ve_bos  bos_ve_bos  bos_bosa_esit
----------  ----------  -------------
1           0           1            

Üç sütun da doğru ya da yanlış üretti; hiçbiri bilinmeyen değil. IS DISTINCT FROM yükleminin desteği motora göre değişir; bulunmayan motorlarda aynı etki, boşluk sınamasını koşula açıkça eklemekle sağlanır.

Boş değerlerin toplama işlevlerindeki davranışı da ayrı bir kural izler ve ikinci konuda ölçülecek: toplama işlevleri boş değerleri sayıma katmaz.

Özet

  • Boş değer, değerin yokluğunu bildiren bir işarettir; sıfır ya da boş dize değildir.
  • Boş değerle yapılan karşılaştırma ne doğru ne yanlış, bilinmeyen üretir; WHERE yalnız doğruyu geçirdiği için hem koşul hem değillemesi satırı eler.
  • Boşluk yalnız IS NULL ve IS NOT NULL ile sınanır.
  • COALESCE boş olmayan ilk argümanı, NULLIF eşitlik durumunda boş değer döndürür; CASE içinde boşluk sınaması eşitlikle değil IS NULL ile yazılır.
  • NOT IN listesinde tek bir boş değer, sorgunun hata vermeden sıfır satır döndürmesine yol açar; aynı liste IN ile kullanıldığında sorun görünmez.
  • IS DISTINCT FROM boş değere duyarsız karşılaştırma sağlar, desteği motora bağlıdır.

Sonraki Adım

Bu derste COALESCE, NULLIF ve CASE yapıları boş değer bağlamında kullanıldı; oysa üçü de daha geniş bir ailenin üyesidir. Sorgu, sütun değerlerini olduğu gibi göstermek zorunda değil — metni kesebilir, sayıyı yuvarlayabilir, tarihten yıl çıkarabilir. Sonraki ders dizgi, sayı ve tarih işlevlerini ele alır ve tarih işlevlerinin adlandırmasının neden standardın en zayıf uygulandığı alan olduğunu gösterir.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat