Ders 08 / 18
Dış Birleştirmeler
Eşleşmesi olmayan satırların korunması, korunan satırların boş değerle dolması, eşleşmeyeni bulan kalıp, sağ ve tam dış birleştirmenin motor desteği ve koşulun yerinin sonucu değiştirmesi.
İçindekiler
Önceki ders iç birleştirmenin bir sınırını gösterdi: koşulu sağlamayan satır sonuçta hiç görünmez. Kitaplar şubelerle birleştirildiğinde şubesi belirlenmemiş kitap düştü, sonuç yedi yerine altı satır oldu.
Kimi sorular tam da o düşen satırları arar. “Hangi şubede hiç kitap yok”, “hangi üye hiç ödünç almamış”, “hangi kitap hiç kimse tarafından alınmamış” — üçü de eşleşmesi olmayan satırları soruyor. İç birleştirme bunları yanıtlayamaz, çünkü aradığı şeyi zaten eliyor. Bu ders eşleşmesi olmayan satırları koruyan birleştirme türünü kurar.
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
Sol Dış Birleştirme
Sol dış birleştirme (left outer join), soldaki tablonun bütün satırlarını korur. Sağdaki tabloda eşleşme bulunmayan satırlar için sağ tablonun sütunları boş değerle doldurulur.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT k.baslik, s.ad AS sube FROM kitap AS k LEFT JOIN sube AS s ON k.sube_id = s.sube_id ORDER BY k.kitap_id; SQL
baslik sube ----------------- ------------ Körlük Merkez Tutunamayanlar Merkez Kum Kitabı Bahçelievler Yaban Bahçelievler Sessiz Ev Kadıköy Anayurt Oteli Kadıköy Tehlikeli Oyunlar
Yedi kitabın hepsi döndü. Tehlikeli Oyunlar satırında şube adı boş: kitap korundu, ama
eşleşme olmadığı için sağ tablodan gelen sütun dolduramadı. OUTER sözcüğü isteğe
bağlıdır; LEFT JOIN ile LEFT OUTER JOIN aynı şeydir.
Boş değerin kaynağı burada önemlidir. Şube adı sütunu NOT NULL tanımlı olduğu için şube
tablosunda hiç boş ad yok. Sonuçtaki boşluk veriden gelmiyor, birleştirmenin kendisi
üretti. Dış birleştirme sonucundaki boş değer iki farklı şeyi gösterebilir: ya kaynak
satırda gerçekten boş bir değer vardı, ya da hiç eşleşme yoktu. Ayrım gerektiğinde
eşleşmenin varlığı, sağ tablonun birincil anahtarı sınanarak anlaşılır.
Yön değiştirildiğinde korunan taraf da değişir.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT s.ad AS sube, k.baslik FROM sube AS s LEFT JOIN kitap AS k ON k.sube_id = s.sube_id ORDER BY s.sube_id, k.kitap_id; SQL
sube baslik ------------ -------------- Merkez Körlük Merkez Tutunamayanlar Bahçelievler Kum Kitabı Bahçelievler Yaban Kadıköy Sessiz Ev Kadıköy Anayurt Oteli Konak
Şimdi bütün şubeler görünüyor; hiç kitabı olmayan Konak da listede, kitap sütunu boş.
Aynı iki tablo, aynı koşul, farklı korunan taraf.
Eşleşmeyeni Bulmak
Dış birleştirmenin en çok kullanılan kalıbı, korunan ama eşleşmeyen satırları ayıklamaktır. Eşleşme yoksa sağ tablonun bütün sütunları boş olacağına göre, sağ tablonun birincil anahtarını boşluk için sınamak bu satırları seçer.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT s.ad AS kitapsiz_sube FROM sube AS s LEFT JOIN kitap AS k ON k.sube_id = s.sube_id WHERE k.kitap_id IS NULL; SQL
kitapsiz_sube ------------- Konak
Aynı kalıp üyelere uygulanır.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT u.ad, u.soyad FROM uye AS u LEFT JOIN odunc AS o ON o.uye_id = u.uye_id WHERE o.odunc_id IS NULL; SQL
ad soyad ----- ----- Burak Şahin
Sınamanın birincil anahtar üzerinde yapılması gerektiğine dikkat edilmelidir. Boş değer kabul eden bir sütun sınansaydı sonuç yanlış olurdu: o sütunun gerçekten boş olduğu eşleşen satırlar da listeye karışırdı. Birincil anahtar hiçbir zaman boş olamayacağı için, boş görünmesi tek bir anlama gelir — eşleşme yok.
Sağ ve Tam Dış Birleştirme
Sol dış birleştirmenin aynadaki karşılığı sağ dış birleştirmedir (right outer join): sağdaki tablonun satırları korunur.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT s.ad AS sube, k.baslik FROM kitap AS k RIGHT JOIN sube AS s ON k.sube_id = s.sube_id ORDER BY s.sube_id, k.kitap_id; SQL
sube baslik ------------ -------------- Merkez Körlük Merkez Tutunamayanlar Bahçelievler Kum Kitabı Bahçelievler Yaban Kadıköy Sessiz Ev Kadıköy Anayurt Oteli Konak
Sonuç, bir önceki bölümdeki sube LEFT JOIN kitap sorgusuyla aynı. Bu bir rastlantı
değil: her sağ dış birleştirme, tablo sırası ters çevrilerek sol dış birleştirmeye
dönüştürülebilir. İki yazım arasında anlam farkı yoktur.
Bu eşdeğerlik pratik bir değer taşır, çünkü sağ dış birleştirme desteği motora göre
değişir — bazı motorlar yalnız sol dış birleştirmeyi gerçekleştirir. Sorgu sol biçimde
yazıldığında taşınabilirlik sorunu ortadan kalkar. Okunabilirlik açısından da sol biçim
tercih edilir: korunan tablo FROM yan tümcesinde, yani okuma sırasında ilk görülen yerde
durur.
Tam dış birleştirme (full outer join) iki tarafın da eşleşmeyen satırlarını korur.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT s.ad AS sube, k.baslik FROM kitap AS k FULL OUTER JOIN sube AS s ON k.sube_id = s.sube_id ORDER BY s.sube_id, k.kitap_id; SQL
sube baslik
------------ -----------------
Tehlikeli Oyunlar
Merkez Körlük
Merkez Tutunamayanlar
Bahçelievler Kum Kitabı
Bahçelievler Yaban
Kadıköy Sessiz Ev
Kadıköy Anayurt Oteli
Konak
Sekiz satır: altı eşleşme, şubesi olmayan bir kitap ve kitabı olmayan bir şube. İki yönün eksikleri tek sonuçta toplandı.
Tam dış birleştirmenin desteği de motora göre değişir ve sağ dış birleştirmeden daha seyrek bulunur. Bulunmadığı yerde eşdeğer yazım, sol dış birleştirme ile ters yönlü sol dış birleştirmenin birleşimidir; küme işlemleri bu konunun son dersinde ele alınacak.
Koşulun Yeri Sonucu Değiştirir
Dış birleştirmede en sık yapılan yanlış, sağ tabloya ait bir koşulu ON yerine WHERE
içine yazmaktır. İç birleştirmede iki yazım aynı sonucu verir; dış birleştirmede vermez.
Önce koşul birleştirmenin içinde:
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT u.ad, u.soyad, o.odunc_id, o.alis_tarihi FROM uye AS u LEFT JOIN odunc AS o ON o.uye_id = u.uye_id AND o.alis_tarihi >= '2025-05-01' ORDER BY u.uye_id, o.odunc_id; SQL
ad soyad odunc_id alis_tarihi ------ ------ -------- ----------- Ayşe Demir 9 2025-05-14 Mehmet Kaya 11 2025-06-11 Zeynep Arslan Emre Yıldız 12 2025-06-20 Selin Aydın 8 2025-05-02 Selin Aydın 10 2025-06-03 Burak Şahin
Altı üyenin hepsi listede. Koşulu sağlayan ödünç kaydı olmayan iki üye korundu ve ödünç sütunları boş kaldı.
Şimdi aynı koşul süzme yan tümcesinde:
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT u.ad, u.soyad, o.odunc_id, o.alis_tarihi FROM uye AS u LEFT JOIN odunc AS o ON o.uye_id = u.uye_id WHERE o.alis_tarihi >= '2025-05-01' ORDER BY u.uye_id, o.odunc_id; SQL
ad soyad odunc_id alis_tarihi ------ ------ -------- ----------- Ayşe Demir 9 2025-05-14 Mehmet Kaya 11 2025-06-11 Emre Yıldız 12 2025-06-20 Selin Aydın 8 2025-05-02 Selin Aydın 10 2025-06-03
İki üye kayboldu. Nedeni beşinci dersteki kuraldır: birleştirme korunan satırların ödünç
sütunlarını boş değerle doldurdu, WHERE yan tümcesi ise bu satırlarda
NULL >= '2025-05-01' karşılaştırmasını değerlendirdi ve sonuç bilinmeyen çıktı. WHERE
yalnız doğruyu geçirir, dolayısıyla korunan satırlar elendi.
Kural şöyle özetlenir: ON yan tümcesi hangi satırların eşleştiğini, WHERE yan
tümcesi birleştirme bittikten sonra hangi satırların kalacağını belirler. Sağ tabloya
ait bir koşul WHERE içine yazıldığında dış birleştirme sessizce iç birleştirmeye döner.
Bu dönüşümün tek istisnası, kasten yazılan boşluk sınamasıdır — eşleşmeyeni bulan kalıp tam
da bu etkiyi kullanır.
Özet
- Sol dış birleştirme soldaki tablonun bütün satırlarını korur ve eşleşme yoksa sağ tablonun sütunlarını boş değerle doldurur.
- Sonuçtaki boş değer ya kaynaktan gelir ya da birleştirmenin kendisi üretmiştir; ayrım sağ tablonun birincil anahtarı sınanarak yapılır.
- Eşleşmeyen satırları bulan kalıp, dış birleştirmeden sonra sağ tablonun birincil anahtarını boşluk için sınamaktır.
- Her sağ dış birleştirme, tablo sırası ters çevrilerek sol dış birleştirmeye dönüşür; sağ ve tam dış birleştirmenin desteği motora göre değişir.
- Tam dış birleştirme iki tarafın da eşleşmeyen satırlarını korur.
- Sağ tabloya ait koşul
WHEREiçine yazıldığında dış birleştirme sessizce iç birleştirmeye döner; koşulONiçinde yazılmalıdır.
Sonraki Adım
Buraya kadarki birleştirmelerin hepsi bir eşitlik koşuluna dayandı. Koşulun kendisi kaldırılırsa ne olur, ya da bir tablo kendisiyle birleştirilirse? Sonraki ders bu iki özel durumu ele alır: koşulsuz birleştirmenin ürettiği çarpım ve bir tablonun iki farklı takma adla kendisine bağlanmasıyla kurulan karşılaştırmalar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.