28 Haziran 2015 Pazar

Temel SQL Dersi 18: JOIN Elemanı 3



Join Elemanı 3


Bildiğiniz gibi üzerinde çalıştığımız veritabanları için kullanılan genel isim “ilişkisel veritabanı”dır. Yani tablolar birbiri ile ilişkilidir ve bu ilişki bir tablodaki birincil anahtar (primary key) ile diğer tablodaki yabancı anahtar (foreign key) üzerinden kurulur. Aşağıdaki okul veritabanında bu tablolar arasındaki ilişkiler açıkça görülüyor.
okul_vt_hrzafer
Join türlerini örneklendirmek için yukarıdaki tablolardan Öğrenci ve Bölüm tablolarını kullanalım:
Inner Join
Inner join en çok kullanılan join türüdür ve her iki tablodaki ortak kayıtları döndürür. Bir başka ifade ile iki tablonun kesişimini döndürür. Mesela bölüm ve öğrenci tablolarını birleştirmek istersek
SELECT * FROM bolum b INNER JOIN ogrenci o ON b.bid = o.bid
Sorgunun sonuç kümesi aşağıdaki gibi olacaktır.
bolum_ogrenci_inner
Sonuç kümesindeki ilk 4 kolon bölüm tablosunda sonraki 5 kolon ise öğrenci tablosuna aittir. Her iki tablonun bid alanlarına bakarsanız bire-bir eşleştiklerini görürsünüz. Yani sorgu iki tablonun kesişimini döndürmüş oluyor.
kümeler_inner

Left ve Right Outer join
Eğer bir tablodaki tüm kayıtlar ile diğer tablodaki birleştirme koşulunu sağlayan kayıtları döndürmek istersek right ve veya left outer join kullanırız.  Mesela tüm bölümleri (hiç öğrencisi olmayanlar da dahil) ve bir bölüme kayıtlı öğrencileri sorgulamak istersek:
SELECT * FROM bolum b LEFT JOIN ogrenci o ON b.bid = o.bid
bolum_ogrenci_left_outer
Son kayıda dikkat ederseniz hiç öğrencisi olmayan bölüm de sonuç kümesinde mevcut.  Yani soldaki tablodan (bölüm) tüm kayıtlar, sağdaki tablodan ise sadece kesişen kayıtlar gelmiş oldu.
kumeler_left_outer
Yukarıdaki sorguyuda  left (sol) yerine right (sağ) sözcüğünü kullanmış olsaydık, tüm öğrenciler ile en az bir öğrencisi olan bölümler seçilirdi. Yani left ve right tüm kayıtların seçileceği tablonun sağdaki mi yoksa soldaki mi olacağını belirtiyor yalnzıca.
SELECT * FROM bolum b RIGHT JOIN ogrenci o ON b.bid = o.bid
Bu sorguda tabloların yerini değiştirisek tekrar tüm bölümleri ve bir bölüme kayıtlı öğrencileri seçmiş oluruz.
SELECT * FROM ogrenci o RIGHT JOIN bolum b  ON b.bid = o.bid
Sadece sonuç kümesinde tabloların yeri değişmiş olur. Aşağıda görüldüğü gibi:
bolum_ogrenci_right_outer
Eğer yalnızca hiç öğrencisi olmayan bölümleri seçmek istersek where ifadesi ile öğrenci tablosundaki tüm kayıtları aşağıdaki gibi eleyebiliriz.
SELECT * FROM bolum b LEFT JOIN ogrenci o ON b.bid = o.bid WHERE o.bid IS NULL
bolum_ogrenci_left_only
Yaptığımız işlemi Venn şeması ile gösterecek olursak:
kumeler_left_only

Full Outer Join
Tüm öğrencileri ve tüm bölümleri seçmek istersek full outer  join kullanırız.
SELECT * FROM bolum b FULL OUTER JOIN ogrenci o ON b.bid = o.bid
Full outer join çok da gerekli olmadığından MySQL’de mevcut değildir. Ancak aşağıdaki gibi bir sorgu ile (union kullanarak) aynı sonuç elde edilebilir.
SELECT * FROM bolum b LEFT JOIN ogrenci o ON b.bid = o.bid
UNION
SELECT * FROM bolum b RIGHT JOIN ogrenci o ON b.bid = o.bid
Yaptığımız işlemi Venn şeması ile gösterecek olursak:
kumeler_full_outer
Sintaks
SQL join sintaksı ile ilgili olarak şunları hatırlatmakta yarar var.
– Inner Join yerine sadece Join yazmamız yeterli
– Left Outer Join yerine sadece Left Join yazmamız yeterli
– Right Outer Join yerine sadece Right Join yazmamız yeterli
Natural Join
Bir de natural join var ki yazımda kolaylık sağladığı için öğrenciler tarafından tercih edilebiliyor.  Mesela aşağıdaki iki sorgu aynı işi yapar:
SELECT * FROM bolum b NATURAL JOIN ogrenci o
SELECT * FROM bolum b RIGHT JOIN ogrenci o ON b.bid = o.bid
Ancak gerçek hayatta Natural Join kullanımı sakıncalıdır.  Çünkü Natural Join iki tablodaki aynı isme sahip tüm alanların (kolonların) eşleşip eşleşmediğine bakar. Öğrenci ve Bölüm tablolarında sadece bid alanları aynı isme sahip olduğundan bir problem çıkarmıyor ancak Bölüm tablosunda bölümün ismini tutan için badi alanı adi şeklinde isimlendirilseydi Öğrenci tablosunda da aynı isimli bir alan bulunduğundan sorgu bu kez aşağıdaki sorgu ile eşdeğer olacaktı.
SELECT * FROM bolum b RIGHT JOIN ogrenci o ON b.bid = o.bid AND o.adi=b.adi

Temel SQL Dersi 17: Örnek Veritabanı


Örnek Veritabanı


Okul veritabanının bu ilk versiyonu 6 tablodan oluşuyor. Tabloların açıklamaları ve ilişkileri aşağıdaki gibidir.
okul_vt_hrzafer
Öğrenci ile Bölüm arasında bire-çok ilişki vardır. Yani bir öğrencinin bir ve yalnız bir bölümü olur.
Aynı şekilde Ders ile Bölüm arasında ve Öğretmen ile Bölüm arasında da bire-çok ilişki var. Yani her ders ve öğretmen bir bölüme ait olmak zorunda.
Öğrenci ile Ders arasında çoğa-çok ilişki vardır. Yani bir öğrenci birden fazla ders alabilir. Bir dersi de birden fazla öğrenci alabilir. Bu nedenle bu ilişki ögrenci_ders adındaki ayrı bir tabloda ele alındı.
Aynı şekilde Öğretmen ile Ders arasında da çoğa çok ilişki vardır. Yani bir dersi birden fazla öğretmen verebilir. Ve bir öğretmen birden fazla ders verebilir.
Veritabanının yapısı (structure) bu şekilde. Bu yapıyı daha iyi anlamak için bazı tabloları girilen örnek verilerle birlikte görelim:
Bölüm Tablosu
okul_vt_tablo_bolum_hrzafer
Ders Tablosu
okul_vt_tablo_ders_hrzafer
Görüldüğü gibi bu tabloda son kolon dersin ait olduğu bölümün id’sini (bid) tutuyor. Yani yabancı anahtar.
Öğrenci Tablosu
okul_vt_tablo_ogrenci_hrzafer
Bu tabloda da son kolon öğrencinin ait olduğu bölümü gösteren bir yabancı anahtar.
Öğrenci Ders Tablosu
okul_vt_tablo_ogrenci_ders_hrzafer
Bu tabloda mesela birinci satır 1 no’lu öğrencinin 1 id’li dersi aldığını ve bu dersten notunun 3 olduğunu belirtiyor.

Temel SQL Dersi 16: HAVING Elemanı



HAVING


HAVING ifadesi bu iki yazıda anlatılanların devamı, çünkü sadece GROUP BY ifadesi ile birlikte kullanılan bir ifade. Aslında HAVING ifadesinin işlevi WHERE ifadesininkine çok benziyor. Ancak kümeleme fonksiyonları ile WHERE ifadesi birlikte kullanılamadığından HAVING ifadesine ihtiyaç duyulmuştur. Meseleyi daha iyi izah edebilmek için hemen örneklere geçelim. Öncelikle bir önceki makalede de kullandığımız aşağıdaki kisiler tablosunu inceleyelim. Sonra bu tablo üzerinden örneklerle konuyu inceleyeceğiz.
idAdSoyadYasCinsiyetŞehirÜlkeMaaş
2AhmetYılmaz201AnkaraTürkiye2000
3MehmetEfe221BoluTürkiye2000
4AyşeCan230İstanbulTürkiye3500
5FatmaAk350AnkaraTürkiye3200
6JohnSmith451New YorkUSA3500
7EllenSmith400New YorkUSA3500
8HansMüller301BerlinAlmanya4000
9FrankCesanne351ParisFransa3700
10AbbasDemir261AdanaTürkiye2000
11HaticeTopçu260HatayTürkiye2200
12GülsümDemir350AdanaTürkiye2000

Öncelikle aşağıdaki 2 sorgu örneğini inceleyelim:
SELECT ülke, AVG(maaş) FROM `kisiler` GROUP BY ülke
Bu sorgu ülkelere göre maaş ortalamasını verir ve çıktısı aşağıdaki gibidir:
ülkeAVG(maaş)
Almanya4000.0000
Fransa3700.0000
Türkiye2414.2857
USA3500.0000

 SELECT ülke, AVG(maaş) FROM `kisiler` WHERE yas > 30 GROUP BY ülke
Dikkat ederseniz bu sorgunun bir öncekinden tek farkı “WHERE yas > 30″ kısmı. Sorgu yine ülkelere göre maaş ortalamasını hesaplıyor ama bu hesaba yalnızca yaşı 30’dan büyük olan çalışanları katıyor. Sorgunun çıktısı aşağıdaki gibidir:
ülkeAVG(maaş)
Fransa3700.0000
Türkiye2600.0000
USA3500.0000

Şimdi de yaş ortalaması 30’un üzerinde olan ülkelerin maaş ortalamalarını getiren sorguyu yazmaya çalışalım. Burada 30 yaş üstü koşulu çalışanlar için değil ülkeler için (yani gruplar) geçerli. O halde yine yukarıdaki sorgularda olduğu gibi ülkelere göre gruplama yapacağız ama bu sefer koşulu gruplar için yazacağız.
 SELECT ülke, AVG(maaş) FROM `kisiler` GROUP BY ülke HAVING AVG(yas) > 30
Sorgunun çıktısı aşağıdaki gibi olacaktır:
ülkeAVG(maaş)
Fransa3700.0000
USA3500.0000
Sorgunun döndürdüğü sonuç kümesine bakarak iki ülkenin (Türkiye ve Almanya) yaş ortalamalarının 30’dan küçük olduğunu söyleyebiliriz.
Şimdi de çalışan sayısı 1’den fazla olan şehirleri ve çalışan sayılarını getiren sorguyu yazalım.
 SELECT şehir, COUNT(*) FROM `kisiler` GROUP BY şehir HAVING COUNT(*) > 1
Sorgunun çıktısı aşağıdaki gibi olacaktır.
şehirCOUNT(*)
Adana2
Ankara2
New York2

Şimdi yazacağımız sorgunun açıklaması şöyle: Maaş ortalaması 3000’den fazla olan ülkelerdeki erkek çalışanların maaş ortalaması. Burada gruplamanın ülkelere göre yapılacağı ve yalnızca erkek çalışanların hesaba katılacağı açık.
 SELECT ülke, AVG(maaş) FROM `kisiler` WHERE Cinsiyet=1 GROUP BY ülke HAVING AVG(maaş) > 3000
Sorgunun çıktısı aşağıdaki gibidir. Bu sorguda WHERE ve HAVING ifadeleri birlikte kullandığımızdan farklarını rahatça görebiliyoruz.
ülkeAVG(maaş)
Almanya4000.0000
Fransa3700.0000
USA3500.0000
Yukarıdaki sorguyu biraz değiştirelim: Erkek çalışanların sayısı 1’den fazla olan ülkelerin maaş ortalamasını getiren sorgu:
 SELECT ülke, AVG(maaş) FROM `kisiler` WHERE Cinsiyet=1 GROUP BY ülke HAVING COUNT(*) > 1
Sorgunun çıktısı aşağıdaki gibi olacaktır:
ülkeAVG(maaş)
Türkiye2000.0000
Sorguları ve “ne yaptıklarını” dikkatle incelerseniz konuyu daha iyi anlayacaksınız. Bitirmeden önce WHERE ifadesinin her zaman GROUP BY ifadesinden önce geldiğini HAVING’in ise her zaman sonra geldiğini hatırlatalım.