İçeriğe geç
academia.sh

Ders 03 / 18

Koşullar

Karşılaştırma işleçleri, mantıksal bağlaçların önceliği, aralık ve liste denetimleri, örüntü eşleme ile kaçış karakteri ve büyük/küçük harf davranışının motora bağlı olması.

İçindekiler

Önceki ders sonuç kümesinin sütunlarını belirledi: hangi sütunlar, hangi adlarla, hangi ifadelerle. Satırlar konusunda tek bir araç kullanıldı — tek bir eşitlik koşulu. Bir sorgunun döndürdüğü satır sayısını belirleyen asıl yan tümce ise WHERE.

WHERE yan tümcesi, FROM yan tümcesinin ürettiği her satır için bir mantıksal ifade değerlendirir ve yalnızca doğru sonuç veren satırları geçirir. Bu ders o ifadenin yazılabileceği bütün biçimleri kurar: karşılaştırma, bağlaç, aralık, liste ve metin örüntüsü.

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ırma İşleçleri

Altı karşılaştırma işleci vardır: =, <>, <, >, <=, >=. Eşitsizlik işlecinin standart yazımı <> biçimindedir; çoğu motor != yazımını da kabul eder, ancak taşınabilir olan <> yazımıdır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, basim_yili FROM kitap WHERE basim_yili > 1975;
SQL
baslik     basim_yili
---------  ----------
Körlük     1995      
Sessiz Ev  1983      

Yedi kitaptan ikisi döndü. Geri kalan beş kitabın dördü koşulu sağlamıyor; beşincisinin — Anayurt Oteli — basım yılı bilinmiyor. Bilinmeyen bir değerin 1975’ten büyük olup olmadığı da bilinmez, dolayısıyla satır geçmez. Boş değerin koşullardaki davranışı beşinci dersin konusu; şimdilik akılda tutulacak kural şudur: boş değer içeren bir karşılaştırma ne doğru ne yanlıştır, ve WHERE yalnız doğruyu geçirir.

Metin karşılaştırması da aynı işleçlerle yapılır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, yazar FROM kitap WHERE yazar <> 'Oğuz Atay';
SQL
baslik         yazar            
-------------  -----------------
Körlük         José Saramago    
Kum Kitabı     Jorge Luis Borges
Yaban          Yakup Kadri      
Sessiz Ev      Orhan Pamuk      
Anayurt Oteli  Yusuf Atılgan    

Metin karşılaştırmasında iki dizenin eşit sayılıp sayılmayacağını harmanlama (collation) belirler. Harmanlama, karakter sırasını ve büyük/küçük harf ile aksan duyarlılığını tanımlar; öntanımlı harmanlama motora göre değişir. Aynı sorgu bir motorda 'oğuz atay' değerini eşit sayabilir, diğerinde saymayabilir.

Mantıksal Bağlaçlar ve Öncelik

Koşullar AND, OR ve NOT ile birleştirilir. Üçünün önceliği farklıdır: NOT en yüksek, sonra AND, en düşük OR. Bu, Programlama Temelleri kursundaki işleç önceliği kuralının aynısıdır ve aynı hataya yol açar.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, yazar, basim_yili FROM kitap
WHERE yazar = 'Oğuz Atay' OR yazar = 'Orhan Pamuk' AND basim_yili > 1980;
SQL
baslik             yazar        basim_yili
-----------------  -----------  ----------
Tutunamayanlar     Oğuz Atay    1972      
Sessiz Ev          Orhan Pamuk  1983      
Tehlikeli Oyunlar  Oğuz Atay    1973      

Sonuçta 1980 öncesi iki kitap var. Niyet “bu iki yazardan birinin 1980 sonrası kitapları” olsaydı sonuç yanlış olurdu. AND daha yüksek öncelikli olduğu için koşul, “yazar Oğuz Atay ya da (yazar Orhan Pamuk ve yıl 1980’den büyük)” biçiminde ayrıştırıldı. Ayraç niyeti düzeltir.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, yazar, basim_yili FROM kitap
WHERE (yazar = 'Oğuz Atay' OR yazar = 'Orhan Pamuk') AND basim_yili > 1980;
SQL
baslik     yazar        basim_yili
---------  -----------  ----------
Sessiz Ev  Orhan Pamuk  1983      

Kural: AND ile OR aynı koşulda geçtiğinde ayraç yazılır. Öncelik kuralına güvenmek doğru sonuç verse bile, sorguyu okuyanın niyeti çıkarmasını zorlaştırır.

Aralık ve Liste

İki karşılaştırmayla yazılan aralık koşulunun kısa bir yazımı vardır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, basim_yili FROM kitap WHERE basim_yili >= 1970 AND basim_yili <= 1980;
SQL
baslik             basim_yili
-----------------  ----------
Tutunamayanlar     1972      
Kum Kitabı         1975      
Tehlikeli Oyunlar  1973      
sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, basim_yili FROM kitap WHERE basim_yili BETWEEN 1970 AND 1980;
SQL
baslik             basim_yili
-----------------  ----------
Tutunamayanlar     1972      
Kum Kitabı         1975      
Tehlikeli Oyunlar  1973      

İki sorgu aynı satırları döndürüyor. BETWEEN her iki uca da dahildir; 1970 ve 1980 değerleri aralığın içindedir. Bu nokta sık atlanır ve özellikle tarihlerde hataya yol açar: bir ayın tamamını BETWEEN ile seçmek isterken ayın son gününü kapsayan sınırın gün mü yoksa gün ile saat mi olduğu karışır. Zaman bileşeni taşıyan sütunlarda alt sınırı dahil, üst sınırı hariç yazan biçim (>= ve <) daha güvenlidir.

Sonlu bir değer kümesi için IN yazılır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, yazar FROM kitap WHERE yazar IN ('Oğuz Atay','Orhan Pamuk');
SQL
baslik             yazar      
-----------------  -----------
Tutunamayanlar     Oğuz Atay  
Sessiz Ev          Orhan Pamuk
Tehlikeli Oyunlar  Oğuz Atay  

IN, OR ile bağlanmış eşitliklerin kısaltmasıdır. Uzun listelerde okunabilirliği artırır ve motora tek bir küme denetimi olarak planlanabilecek bir yapı sunar. Değillemesi NOT IN biçiminde yazılır ve listede boş değer bulunduğunda beklenmedik davranır; bu tuzak beşinci derste ölçülecek.

Değillemenin Sınırı

NOT bir koşulu tersine çevirir, ama “tersi” sözcüğü boş değer varken yanıltıcıdır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik, sube_id FROM kitap WHERE sube_id = 1;
SELECT baslik, sube_id FROM kitap WHERE NOT (sube_id = 1);
SQL
baslik          sube_id
--------------  -------
Körlük          1      
Tutunamayanlar  1      
baslik         sube_id
-------------  -------
Kum Kitabı     2      
Yaban          2      
Sessiz Ev      3      
Anayurt Oteli  3      

İlk sorgu iki satır, ikincisi dört satır döndürdü. Toplam altı; oysa tabloda yedi kitap var. Kayıp satır Tehlikeli Oyunlar: şube kimliği boş olduğu için ne birinci koşulu ne de değillemesini sağlıyor. Bir koşul ile değillemesinin birlikte tablonun tamamını vermesi, yalnızca ilgili sütunda boş değer yoksa geçerlidir.

Örüntü Eşleme

Metin içinde arama LIKE işleciyle yapılır. İki joker karakter vardır: % sıfır ya da daha çok karakteri, _ tam olarak bir karakteri karşılar.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik FROM kitap WHERE baslik LIKE '_um%';
SQL
baslik    
----------
Kum Kitabı

Örüntü, “herhangi bir karakter, ardından um, ardından herhangi bir şey” diyor ve yalnız bir başlığa uyuyor.

Asıl soru şudur: LIKE büyük/küçük harfe duyarlı mıdır? Yanıt motora göre değişir ve aynı motor içinde bile karakterin ASCII olup olmamasına göre değişebilir.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik FROM kitap WHERE baslik LIKE 'k%';
SQL
baslik    
----------
Körlük    
Kum Kitabı

Küçük harfli k ile yazılan örüntü, büyük K ile başlayan iki başlığı yakaladı: bu motorda ASCII harfler için LIKE harf duyarsızdır. Aynı motor ASCII dışı harflerde ise duyarlıdır.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT baslik FROM kitap WHERE baslik LIKE '%ö%';
SELECT baslik FROM kitap WHERE baslik LIKE '%Ö%';
SQL
baslik
------
Körlük

İkinci sorgu hiç satır döndürmedi. Körlük başlığında ö var, ama Ö ile yazılan örüntü eşleşmedi — harf katlaması yalnız ASCII aralığında uygulandı. Türkçe metinlerde bu, sessiz sonuç kaybının doğrudan kaynağıdır.

Duyarlılığı sorgunun kendisiyle sabitlemek de tam çözüm değildir, çünkü harf dönüştürme işlevleri de aynı sınıra takılabilir.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT UPPER('Körlük') AS buyuk, LOWER('KÖRLÜK') AS kucuk;
SQL
buyuk   kucuk 
------  ------
KöRLüK  kÖrlÜk

Dönüştürme ASCII harfleri değiştirdi, ö ve Ö olduğu gibi kaldı. Doğru çözüm, sorguyu yamamak değil, sütuna harf ve aksan duyarsız bir harmanlama tanımlamak ya da motorun Unicode desteğini etkinleştirmektir; ikisi de motora göre değişir.

Örüntünün kendisinde % ya da _ karakteri aranıyorsa kaçış karakteri bildirilir.

sqlite3 kutuphane.db <<'SQL'
.headers on
.mode column
SELECT 'Indirim %20' AS metin,
       'Indirim %20' LIKE '%!%%' ESCAPE '!' AS kacisli,
       'Indirim %20' LIKE '%%%'             AS kacissiz
UNION ALL
SELECT 'Indirim 20',
       'Indirim 20'  LIKE '%!%%' ESCAPE '!',
       'Indirim 20'  LIKE '%%%';
SQL
metin        kacisli  kacissiz
-----------  -------  --------
Indirim %20  1        1       
Indirim 20   0        1       

ESCAPE '!' bildirimi, !% yazımını “gerçek yüzde işareti” olarak okutur. Kaçışsız örüntü ise her iki metne de uyuyor, çünkü %%% yazımı “herhangi bir şey” demektir. Ölçüt sütunundaki 1 ve 0 değerleri mantıksal doğru ve yanlışın gösterimidir; boole değerinin nasıl yazıldığı da motora göre değişir.

Özet

  • WHERE yalnızca doğru sonuç veren satırları geçirir; ne doğru ne yanlış olan satır elenir.
  • NOT en yüksek, AND orta, OR en düşük önceliklidir; ikisi bir arada geçtiğinde ayraç yazılır.
  • BETWEEN her iki uca dahildir; zaman bileşeni olan sütunlarda alt sınır dahil, üst sınır hariç yazım daha güvenlidir.
  • IN, OR ile bağlanmış eşitliklerin kısaltmasıdır.
  • Bir koşul ile değillemesi tablonun tamamını yalnızca ilgili sütunda boş değer yoksa verir.
  • LIKE işlecinde % ve _ joker karakterlerdir, ESCAPE bunları gerçek karakter yapar; harf duyarlılığı motora ve karakterin ASCII olup olmamasına göre değişir.

Sonraki Adım

Buraya kadar hangi satırların döneceği belirlendi, ama hangi sırayla döneceği hiç belirlenmedi. Çıktılardaki sıra motorun okuma biçiminden geliyor ve garanti değil. Sonraki ders sonuç kümesini sıralayan yan tümceyi, çok anahtarlı sıralamayı, boş değerlerin sıralamadaki yerini ve sonucu ilk N satırla sınırlamanın standart ile yaygın yazımları arasındaki farkı ele alır.

İ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