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;
WHEREyalnız doğruyu geçirdiği için hem koşul hem değillemesi satırı eler. - Boşluk yalnız
IS NULLveIS NOT NULLile sınanır. COALESCEboş olmayan ilk argümanı,NULLIFeşitlik durumunda boş değer döndürür;CASEiçinde boşluk sınaması eşitlikle değilIS NULLile yazılır.NOT INlistesinde tek bir boş değer, sorgunun hata vermeden sıfır satır döndürmesine yol açar; aynı listeINile kullanıldığında sorun görünmez.IS DISTINCT FROMboş 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.