3.5. Tablolar Arası Katılım

Buraya kadar, yaptığımız sorgulamalarda her seferinde sadece bir tabloya erişildi. Oysa sorgulamalar aynı andan birden çok tabloya erişebildiği gibi, aynı tabloya birden fazla kez erişerek satırlara daha çeşitli yaptırımlar uygulayabilir. Aynı anda birden fazla satır ya da birden fazla tabloya erişen sorgulara katılımlı sorgu denir. Sözgelimi (daha önce oluşturduğumuz tablolardaki) tüm illerin hava durumlarını ve konumlarını aynı anda listelemek istiyoruz. Bunun için hava_durumu tablosundaki tüm il sütunları ile iller tablosundaki tüm ad sütunlarını karşılaştırıp, aynı olan satır çiftlerini seçmek gerekir.

Bilginize

Bu sadece kavramsal bir modeldir. Katılımlı sorgular, aslında, her olası satır çiftini karşılaştırmaktan biraz daha verimli bir anlamda uygulanır ama bu işlemi kullanıcı görmez.

Yukarıda bahsedilen işlemi şu sorgu ile elde edebiliriz:

SELECT * FROM hava_durumu JOIN iller ON il = ad;

Sonuç:

      il       | asg_sck | azm_sck | yağış |   tarih    |      ad       |   konum
---------------+---------+---------+-------+------------+---------------+-----------
 San Francisco |      46 |      50 |  0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |     0 | 1994-11-29 | San Francisco | (-194,53)
(2 satır)

Çıktıda dikkat edilmesi gereken iki nokta bulunmakta:

  • Haywrad şehri için hiçbir çıktı alınmadı dikkat edildiyse. Bunun nedeni ise iller tablosunda Hayward adlı bir il olmaması ve dolayısıyla JOIN bu şehri eledi. İleride bunun nasıl düzeltilebileceği üzerinde durulacak.

  • Bir diğer dikkat çeken nokta ise, illerin adını yazan iki tane sütun olması. Bunun sebebi hava_durumu ve iller tablosunun birleştirilmesidir. Pratikte bu istenmeyen bir sonuçtur. Böyle bir durumda buna neden olan * ifadesi yerine açıkça listelenmesini istediğimiz sütunları yazarak bu işi halledebiliriz:

    SELECT il, asg_sck, azm_sck, yağış, tarih, konum
        FROM hava_durumu JOIN iller ON il = ad;
    

Tablolardaki tüm sütun isimleri farklı olduğundan çözümleyici hangi ismin hangi tabloya ait olduğunu bulur. Ama bunu daha da açıkça belirtmek isimler aynı olduğunda dahi sorun çıkmasını önler ve tavsiye edilen de budur:

SELECT hava_durumu.il, hava_durumu.asg_sck, hava_durumu.azm_sck,
       hava_durumu.yağış, hava_durumu.tarih, iller.konum
    FROM hava_durumu JOIN iller ON hava_durumu.il = iller.ad;

Birleştirme sorgusundaki tüm sütun adlarını nitelemek yaygın olarak iyi bir uygulama olarak kabul edilir, böylece daha sonra tablolardan birine yinelenen sütun adlarından biri eklenirse sorgu başarısız olmaz.

Şimdiye kadar gördüğümüz katılım sorguları ayrıca şu şekilde de yazılabilir:

SELECT *
    FROM hava_durumu, iller
    WHERE il = ad;

Bu sözdizimi, SQL-92'de tanıtılan JOIN/ON sözdizimine dayanır. Tablolar basitçe FROM deyiminde listelenir ve karşılaştırma ifadesi WHERE deyimine eklenir. Bu eski örtük sözdiziminden ve daha yeni açık JOIN/ON sözdiziminden elde edilen sonuçlar aynıdır. Ancak sorgunun okuyucusu için, açık sözdizimi anlamını anlamayı kolaylaştırır: Daha önce koşul, diğer koşullarla birlikte WHERE deyimine karıştırılmışken, birleştirme koşulu kendi anahtar sözcüğüyle tanıtılır.

Şimdi Hayward kayıtlarına nasıl kavuşacağımızı işleyeceğiz. İstediğimiz şey hava_durumu tablosu üzerinde tarama yapıp, iller tablosunda bunlarla eşleşen satırları bulmak. Eğer iller tablosunda herhangi bir eşleşme bulamazsak, o sütun iller tablosu alanında boş gözükecek. Bu tür sorgulama işlemleri haricen katılım (outer join) olarak bilinir. (Şimdiye kadar gördüğümüz katılım sorgularında ise hep dahilen katılım (inner join) kullanmıştık.) Komut şöyle görünür:

SELECT *
    FROM hava_durumu LEFT OUTER JOIN iller ON hava_durumu.il = iller.ad;

Sonuç:

      il       | asg_sck | azm_sck | yağış |   tarih    |      ad       |   konum
---------------+---------+---------+-------+------------+---------------+-----------
 San Francisco |      46 |      50 |  0.25 | 1994-11-27 | San Francisco | (-194,53)
 San Francisco |      43 |      57 |     0 | 1994-11-29 | San Francisco | (-194,53)
 Hayward       |      37 |      54 |       | 1994-11-29 |               |
(3 satır)

Bu sorguya sola haricen katılımlı (left outer join) sorgu denir. Böyle adlandırılmasının sebebi soldaki tablonun tüm satırları en az bir kere listelenirken, sağda yer alan tablonun sadece soldaki tablonun satırlarıyla eşleşen satırlarının listelenmesidir. Bir sol-tablo satırı çıktılanırken sağ-tabloda bu satırla eşleşen bir satır yoksa, sağ-tablonun sütunları boş kalır.

Alıştırma:

Ayrıca, sağa haricen katılımlı (right outer join) ve iki yönlü haricen katılımlı (full outer join) sorgu türleri de var. Bunların ne yaptığını da siz bulmayı deneyin.

Ayrıca, bir tabloyu kendine katılımlı olarak da sorgulayabiliriz ve buna kendine katılımlı sorgu denir. Bir örnek olarak, diğer hava durumu kayıtlarının sıcaklık aralığı içinde kalan hava durumu kayıtlarını bulmak isteyelim. Yani, her hava_durumu satırının asg_sck ve azm_sck sütununu diğer hava_durumu satırlarının asg_sck ve azm_sck sütunu ile karşılaştıracağız. Bunu şu sorgu ile yapabiliriz:

SELECT w1.il, w1.asg_sck AS low, w1.azm_sck AS high,
       w2.il, w2.asg_sck AS düşük, w2.azm_sck AS yüksek
    FROM hava_durumu w1 JOIN hava_durumu w2
    ON w1.asg_sck < w2.asg_sck AND w1.azm_sck > w2.azm_sck;

Sonuç:

      il       | low | high |      il       | düşük | yüksek
---------------+-----+------+---------------+-------+--------
 San Francisco |  43 |   57 | San Francisco |    46 |     50
 Hayward       |  37 |   54 | San Francisco |    46 |     50
(2 satır)

Burada katılımın sol ve sağ taraflarını ayırabilmek için hava_durumu tablosunu W1 ve W2 olarak yeniden isimlendirdik. Ayrıca, bu çeşit isimlendirmeleri aynı şeyleri uzun uzadıya yazmaktan kaçınmak için diğer sorgularda da kullanabilirsiniz. Örnek:

SELECT *
    FROM hava_durumu w JOIN iller c ON w.il = c.name;

Bu tarz kısaltmalarla sıkça karşılaşacaksınız.