Ders 07 / 18
İç Birleştirme
Normalleştirilmiş şemada dağılmış bilginin tek sonuçta toplanması, birleştirme koşulu, tablo takma adları, eşleşmeyen satırların düşmesi ve satır sayısının çoğalması.
İçindekiler
Önceki konu tek tablodan okuyan sorguları kurdu: sütun seçimi, koşul, sıralama, boş değer davranışı, işlevler. Bu araçlarla yanıtlanamayan bir soru kaldı. Ödünç tablosuna bakıldığında hangi üyenin hangi kitabı aldığı görünmez.
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
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT odunc_id, kitap_id, uye_id, alis_tarihi FROM odunc WHERE odunc_id <= 4 ORDER BY odunc_id; SQL
odunc_id kitap_id uye_id alis_tarihi -------- -------- ------ ----------- 1 1 1 2025-01-10 2 2 1 2025-02-02 3 1 2 2025-02-11 4 3 3 2025-03-01
Görünen tek şey kimlikler. Bu bir eksiklik değil, normalleştirmenin doğrudan sonucudur: üyenin adı üye tablosunda bir kez yazılır, ödünç kaydında yalnız ona başvuran yabancı anahtar bulunur. Bilgi bilerek dağıtılmıştır; sorgunun işi onu geçici olarak toplamaktır. Toplama işlemine birleştirme (join) denir.
Birleştirme Koşulu
Birleştirme, iki tablonun satırlarını bir koşula göre eşleştirir. Yazımı JOIN ve ON
yan tümceleriyle kurulur.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT o.odunc_id, u.ad, u.soyad, o.alis_tarihi FROM odunc AS o JOIN uye AS u ON o.uye_id = u.uye_id WHERE o.odunc_id <= 4 ORDER BY o.odunc_id; SQL
odunc_id ad soyad alis_tarihi -------- ------ ------ ----------- 1 Ayşe Demir 2025-01-10 2 Ayşe Demir 2025-02-02 3 Mehmet Kaya 2025-02-11 4 Zeynep Arslan 2025-03-01
Kimlikler adlara dönüştü. ON yan tümcesindeki koşul, ödünç kaydındaki yabancı anahtarı
üye tablosundaki birincil anahtarla eşliyor. Bu, birleştirmenin en yaygın biçimidir:
eşitlik koşuluyla yazılan ve iç birleştirme (inner join) adını taşıyan biçim. JOIN
sözcüğü tek başına yazıldığında iç birleştirme anlaşılır; INNER JOIN yazımı da aynı
şeydir.
AS o ve AS u yazımları tablolara takma ad verir. İki tabloda aynı adlı sütunlar
bulunduğunda — burada iki tabloda da uye_id var — hangi tablonun sütunundan söz edildiğini
takma ad belirler. Tek harflik takma adlar yaygındır ama tablo sayısı arttıkça
okunabilirliği düşürür; anlamlı kısaltmalar daha iyidir.
Birden Çok Tablo
Birleştirme zincirlenebilir. Ödünç kaydı hem üyeye hem kitaba başvurduğu için, iki birleştirme sorunun tamamını yanıtlar.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT o.odunc_id, u.ad || ' ' || u.soyad AS uye, k.baslik, o.alis_tarihi FROM odunc AS o JOIN uye AS u ON o.uye_id = u.uye_id JOIN kitap AS k ON o.kitap_id = k.kitap_id ORDER BY o.odunc_id; SQL
odunc_id uye baslik alis_tarihi -------- ------------- ----------------- ----------- 1 Ayşe Demir Körlük 2025-01-10 2 Ayşe Demir Tutunamayanlar 2025-02-02 3 Mehmet Kaya Körlük 2025-02-11 4 Zeynep Arslan Kum Kitabı 2025-03-01 5 Zeynep Arslan Yaban 2025-03-18 6 Emre Yıldız Körlük 2025-04-05 7 Emre Yıldız Sessiz Ev 2025-04-21 8 Selin Aydın Tutunamayanlar 2025-05-02 9 Ayşe Demir Tehlikeli Oyunlar 2025-05-14 10 Selin Aydın Kum Kitabı 2025-06-03 11 Mehmet Kaya Anayurt Oteli 2025-06-11 12 Emre Yıldız Yaban 2025-06-20
Ödünç tablosundaki on iki kaydın hepsi döndü. Her birleştirme sonucu yeni bir bağıntıdır ve bir sonraki birleştirmenin girdisi olur; sıra, yazıldığı sıradır ama motorun uyguladığı sıra farklı olabilir — bildirimsel dilin kuralı burada da geçerlidir.
Eşleşmeyen Satırlar Düşer
İç birleştirmenin tanımlayıcı özelliği, koşulu sağlamayan satırların sonuçta hiç görünmemesidir. Kitapları şubeleriyle birleştiren sorgu bunu gösteriyor.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT k.baslik, s.ad AS sube FROM kitap AS k 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
Altı satır döndü, oysa kitap tablosunda yedi kitap var. Eksik olan Tehlikeli Oyunlar:
şube kimliği boş olduğu için k.sube_id = s.sube_id koşulu hiçbir şube satırı için doğru
olmuyor. Beşinci dersteki kural burada da işliyor — boş değerle yapılan eşitlik ne doğru ne
yanlış üretir, ve birleştirme de yalnız doğruyu geçirir.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT COUNT(*) AS kitap FROM kitap; SELECT COUNT(*) AS birlestirme_sonucu FROM kitap AS k JOIN sube AS s ON k.sube_id = s.sube_id; SQL
kitap ----- 7 birlestirme_sonucu ------------------ 6
Kaybın sessizliği önemlidir: sorgu hata vermez, bir eksik döndürür. Rapor sayıları tutmadığında ilk bakılacak yerlerden biri, iç birleştirmenin elediği satırlardır. Eşleşmeyen satırların da korunması gerekiyorsa dış birleştirme yazılır; sonraki dersin konusu budur.
Satır Sayısı Artabilir de
Birleştirme yalnız satır düşürmez, çoğaltabilir de. Bir tablodaki bir satır, diğer tabloda birden çok satırla eşleşiyorsa sonuçta o kadar kez görünür.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT k.baslik, o.odunc_id, o.alis_tarihi FROM kitap AS k JOIN odunc AS o ON k.kitap_id = o.kitap_id WHERE k.baslik = 'Körlük' ORDER BY o.odunc_id; SQL
baslik odunc_id alis_tarihi ------ -------- ----------- Körlük 1 2025-01-10 Körlük 3 2025-02-11 Körlük 6 2025-04-05
Kitap tablosunda tek olan satır, ödünç kayıtlarıyla birleştiğinde üç kez göründü. Bu
doğrudur — üç ayrı ödünç işlemi var — ama sayım yapılırken tuzağa dönüşür: bu sonuç
üzerinden kitap sayılırsa Körlük üç kez sayılır. Toplama işlevleri konusunda bu ayrım
tekrar ele alınacak.
Genel kural: bire-çok bir ilişkide birleştirme, “bir” tarafındaki satırları “çok” tarafındaki eşleşme sayısı kadar tekrarlar.
Kısaltılmış Yazımlar
Birleştirmenin daha kısa iki yazımı vardır ve ikisi de dikkat ister.
Birincisi, birleştirme koşulunu iki tabloda aynı adı taşıyan sütuna dayandıran USING
yan tümcesidir.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT baslik, ad AS sube FROM kitap JOIN sube USING (sube_id) ORDER BY 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
USING (sube_id) yazımı ON k.sube_id = s.sube_id ile aynı sonucu verir ve ortak sütunu
sonuçta bir kez gösterir. Adlandırma tutarlıysa yararlıdır; sütun adları farklıysa
kullanılamaz.
İkincisi, birleştirme koşulunu hiç yazmayan doğal birleştirmedir (natural join): iki tabloda aynı adı taşıyan bütün sütunlar birleştirme koşulu sayılır. Kısalığı, sessiz yanlışlık pahasına gelir.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT 'uye NATURAL JOIN sube satır sayısı: ' || COUNT(*) AS olcum FROM uye NATURAL JOIN sube; SQL
olcum ------------------------------------- uye NATURAL JOIN sube satır sayısı: 0
Üye ile şube tablosu arasında hiçbir anlamlı ilişki yok, ama ikisinde de ad adlı bir sütun
var. Doğal birleştirme bunu bir eşleştirme ölçütü sanıp üye adı ile şube adını karşılaştırdı
ve sıfır satır döndürdü. Hata iletisi yok; yalnız boş bir sonuç.
Asıl tehlike, şemaya sonradan eklenen bir sütunun adının bir başka tabloyla çakışmasıdır: o gün doğru çalışan doğal birleştirme, ertesi gün sessizce başka bir şey hesaplamaya başlar. Birleştirme koşulunu açıkça yazmak, bu sınıf hatayı tümüyle ortadan kaldırır.
Eski bir yazım daha vardır: tabloları virgülle ayırıp koşulu WHERE içine koymak.
sqlite3 kutuphane.db <<'SQL' .headers on .mode column SELECT k.baslik, s.ad AS sube FROM kitap AS k, sube AS s WHERE 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
Sonuç aynı, ama iki kusuru var. Birleştirme koşulu ile süzme koşulu aynı yan tümcede
karışır; ve koşul unutulduğunda sorgu hata vermeden bütün eşleşme çiftlerini üretir. ON
yazımı koşulu birleştirmenin yanında tutarak her ikisini de önler.
Özet
- Normalleştirilmiş şemada bilgi bilerek dağıtılır; birleştirme onu sorgu süresince geçici olarak toplar.
- İç birleştirme yalnız koşulu doğru olan satır çiftlerini döndürür; eşleşmeyen satır hata vermeden düşer.
- Boş değer taşıyan yabancı anahtar hiçbir satırla eşleşmez ve iç birleştirmede kaybolur.
- Bire-çok ilişkide birleştirme, “bir” tarafındaki satırı eşleşme sayısı kadar tekrarlar.
- Tablo takma adları aynı adlı sütunları ayırt eder ve çok tablolu sorguları okunabilir kılar.
USINGortak adlı sütunda kısalık sağlar; doğal birleştirme koşulu ad benzerliğine bıraktığı için sessiz yanlışlığa açıktır.
Sonraki Adım
İç birleştirmenin eşleşmeyen satırı düşürmesi, kimi sorularda tam olarak istenmeyen şeydir: “hiç kitabı olmayan şube” ya da “hiç ödünç almamış üye” gibi sorular, tam da eşleşmesi olmayan satırları arar. Sonraki ders bu satırları koruyan dış birleştirmeleri ele alır, korunan satırların boş değerle nasıl dolduğunu gösterir ve sağ ile tam dış birleştirmenin motor desteğine bağlı olduğunu ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.