Bu makalede genelde iliÅŸkisel veritabanı [Relational Database] sistemlerinin özelde SQL Server’in temel konularından biri olan JOIN seçeneklerini özetleyeceÄŸiz. SQL Server ne tür JOIN seçenekleri sunuyor, bu seçeneklerin birbirinden farkı nedir, olay sadece syntax farklılığı mıdır, performans cephesinden neler oluyor . . . bu soruları yanıtlayacağız. Table veya view gibi kaynakları birbirine baÄŸlayan JOIN ifadeleri bu kaynaklara uygulanmış SELECT, INSERT…SELECT, SELECT…INTO, UPDATE ve DELETE iÅŸlemlerinde FROM deyimiyle birlikte kullanılır.
Yazının devamına geçmeden önce örnek olarak kullanacağımız tabloları oluşturalım. Örnek bir Öğrenci İşleri projesinde öğrencilerden sorumlu danışman öğrentim üyelerini sakladığımız tablolar yazacağız. Bunun için Ogrenci, Danisman ve bu iki tablo arasındaki ilişkinin tutulduğu OgrenciDanisman tablolarını oluşturacağız.
--Öğrenci tablosu CREATE TABLE Ogrenci( OgrenciId int, AdSoyad varchar(100) ) --Danışman tablosu CREATE TABLE Danisman( DanismanId int, AdSoyad varchar(100) ) --Öğrenci Danışman tablosu CREATE TABLE OgrenciDanisman( OgrenciDanismanId int, OgrenciId int, DanismanId int ) --Ogrenci tablosuna kayıt girelim INSERT Ogrenci VALUES(1,'Öğrenci_1') INSERT Ogrenci VALUES(2,'Öğrenci_2') INSERT Ogrenci VALUES(3,'Öğrenci_3') INSERT Ogrenci VALUES(4,'Öğrenci_4') --Danışman tablosuna kayıt girelim INSERT Danisman VALUES(1,'Danışman_1') INSERT Danisman VALUES(2,'Danışman_2') INSERT Danisman VALUES(3,'Danışman_3') INSERT Danisman VALUES(4,'Danışman_4') --Danışmanı olan öğrenciler için kayıt girelim --1 nolu öğrencinin danışmanı=2 nolu danışman INSERT OgrenciDanisman VALUES(1,1,2) --3 nolu öğrencinin danışmanı=2 nolu danışman INSERT OgrenciDanisman VALUES(2,3,2) --4 nolu öğrencinin danışmanı=1 nolu danışman INSERT OgrenciDanisman VALUES(3,4,1)
Bu tablolardaki veriler aşağıdaki gibi olacaktır;

JOIN yöntemlerini iki veya daha fazla tabloyu aynı anda sorgulamak için tercih ederiz. Sorguda kullanılan tablolar tamamen ayrı tablolar olduğu gibi aynı tablo da olabilir (SELF JOIN). JOIN yazım biçimi genel olarak aşağıdaki gibidir;
FROM ilk_table join_turu ikinci_table [ON (join_kosulu)]
join_turu ifadesi hangi JOIN seçeneğinin (inner, outer veya cross join) kullanılacağını, join_kosulu ise her iki tabloya ait satırların hangi sınırlamayla ilişkilendirileceğini belirtir. Aşağıdaki örnekte FROM bölümünde JOIN yapısı belirtilmiştir.
FROM Ogrenci JOIN OgrenciDanisman
ON (Ogrenci.OgrenciId = OgrenciDanisman.OgrenciId)
Birleştirilen iki tablodan hangisinin baskın olacağı veya hangisinden hangi alanların okunacağı tercih edilien JOIN türüne göre değişkenlik arzeder. Bu amaçla SQL Server aşağıdaki üç temel JOIN türlerini destekler.
- INNER JOIN (İç BirleÅŸtirici): SQL Server’de varsayılan seçenek olup iki tabloyu JOIN ifadesinde belirtilmiÅŸ olan kolon veya kolonların eÅŸitliÄŸine göre birleÅŸtirir. Bu seçenekte tablolar her iki tarafta ortak olan kolonlar üzerinden birleÅŸtirilir. Tablo arasındaki eÅŸitlik için genellikle = veya <> operatorleri kullanılır. ÖrneÄŸin hem Ogrenci hem de OgrenciDanisman tablosunda olan öğrenci kayıtlarını çekmek için INNER JOIN türü kullanılır. Yani bire bir iliÅŸki için tercih edilir. Yazım biçimi olarak INNER JOIN yazılabileceÄŸi gibi sadece JOIN sözcüğü de yazılabilir.
Aşağıdaki örnekte danışmanı olan öğrenciler listelenmiştir. JOIN işlemlerinde yazım biçimini daha kısa tutmak ve anlaşılır hale getirmek için tablolar için alias tanımlanır.
SELECT O.* FROM Ogrenci O INNER JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId

Bu örnekte O.* ifadesiyle Ogrenci tablosundaki tüm alanlar listelenmiştir. Örneğin sadece öğrencinin adını listelemek istemiş olsaydı O.AdSoyad yazmamız yeterli olacaktır. JOIN işlemini 3 tabloya da uygulayabiliriz. Öğrencilerin hangi danışmanlara atandığını listeleyelim. Öğrencinin adı, Ogrenci tablosundan danışmanın adı da Danisman tablosundan alınmıştır.
SELECT O.OgrenciId,O.AdSoyad,D.DanismanId,D.AdSoyad FROM Ogrenci O INNER JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId INNER JOIN Danisman D ON OD.DanismanId=D.DanismanId

- OUTER JOIN (Dış Birleştirici): INNER JOIN gibi iki tablo arasında ortak kolonlar üzerinden bağlantı kurar farklı olarak tablolarda karşılığı olmayan satırlar da gelir. İkinci tabloda karşılığı olmayan satırlar NULL olarak döner. Bu JOIN seçeneğini daha çok bir tabloda olup diğer tabloda olmayan kayıtları listelemek için tercih ederiz. OUTER JOIN seçeneğinde hangi tablonun baskın olacağı aşağıdaki ifadelerle belirtilir. Bu JOIN seçeneğinde karşılığı olmayan satırlar WHERE bölümünde ilgili kolonların null değeriyle karşılaştırılmasıyla bulunur.
- LEFT JOIN veya LEFT OUTER JOIN : Bu seçenekte ifadenin solunda ismi yazan tablodaki tüm satırlar listelenir, sağ taraftaki tabloya ait kolonlar ise null olarak döner.
Aşağıdaki sorguda Ogrenci ve Danisman tabloları arasında LEFT JOIN işlemi uygulanmıştır.
SELECT * FROM Ogrenci O LEFT JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId

Sonuç listesinde görüldüğü gibi 2 nolu öğrenci herhangi bir danışmana bağlı olmadığı için o satıra karşışık OgrenciDanisman tablosundaki kolonlar null dönmüştür. Bu alanlardan birini filtreleyerek herhangi bir danışmana atanmamış öğrencileri bulabiliriz.
SELECT * FROM Ogrenci O LEFT JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId WHERE OD.DanismanId IS NULL

- RIGHT JOIN veya RIGHT OUTER JOIN : Bu seçenekte ifadenin sağında ismi yazan tablodaki tüm satırlar listelenir, sol taraftaki tabloya ait kolonlar ise null olarak döner.
Önceki örneğe benzer olarak öğrencisi olmayan danışmanları listeleyebiliriz. Bunun için ilgili tabloları yazdıktan sonra LEFT JOIN yerine RIGHT JOIN ifadesini kullanıp WHERE bölümünü aşağıdaki gibi düzeltmemiz yeterli olacaktır.
SELECT * FROM OgrenciDanisman OD RIGHT JOIN Danisman D ON D.DanismanId=OD.DanismanId WHERE OD.DanismanId IS NULL

- FULL JOIN veya FULL OUTER JOIN : Bu seçenekte hem sağ hem de soldaki tablonun tüm satırları listelenir. Her iki tabloda da karşılığı olmayan satırlar null olarak döner. Aşağıdaki örnekte 3 tabloyu birbiriyle FULL JOIN yaparak Ogrenci ve Danisman tablolarındaki karşılıksız satırları listelemiş olduk.
SELECT * FROM Ogrenci O FULL JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId FULL JOIN Danisman D ON D.DanismanId=OD.DanismanId

- LEFT JOIN veya LEFT OUTER JOIN : Bu seçenekte ifadenin solunda ismi yazan tablodaki tüm satırlar listelenir, sağ taraftaki tabloya ait kolonlar ise null olarak döner.
- CROSS JOIN (Çapraz Birleştirici): Kartezyen çarpım (cartesian product) olarak ta bilinen bu seçenekte ortak bir alan belirtilmesine gerek kalmadan iki tablo arasında tüm eşleştirmeleri listeler. Bunu da soldaki tablodaki herbir satıra karşılık sağdaki tablonun tüm satırlarını döndürerek gerçekleştirir.
Ogrenci ve Danisman tabloları arasında CROSS JOIN kuralım. Bu durumda her öğrenciye karşılık tüm danışmanlar listelenecektir.
SELECT * FROM Ogrenci O CROSS JOIN Danisman D ORDER BY OgrenciId
Internet’ten alınmış olan aÅŸağıdaki ÅŸekilde JOIN türlerinin anlaşılması açısından kolaylık saÄŸlamaktadır.

JOIN işlemlerinin sadece SELECT tümcesinde değil DELETE, UPDATE ile birlikte de kullanabiliriz. Hatta ciddi bir kolaylık ta sağlayacaktır. Örnek olarak herhangi bir danışmanı olmayan öğrencileri silelim. Öncelikle sonucu getiren SELECT cümlesini yazalım.
SELECT * FROM Ogrenci O LEFT JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId WHERE OD.DanismanId IS NULL
Bu cümleyi aşağıdaki gibi değiştirmemiz silme işlemi için yeterli olacaktır.
DELETE Ogrenci FROM Ogrenci O LEFT JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId WHERE OD.DanismanId IS NULL
Yeri gelmişken yazalım. Bu sorguya alternatif olarak aşağıdaki gibi iç içe sorgu yöntemi de kullanılabilir.
DELETE Ogrenci WHERE OgrenciId IN ( SELECT O.OgrenciId FROM Ogrenci O LEFT JOIN OgrenciDanisman OD ON O.OgrenciId=OD.OgrenciId WHERE OD.DanismanId IS NULL ) --VEYA DELETE Ogrenci FROM Ogrenci O WHERE NOT EXISTS (SELECT * FROM OgrenciDanisman WHERE OgrenciId=O.OgrenciId) --VEYA DELETE Ogrenci WHERE NOT OgrenciId IN (SELECT OgrenciId FROM OgrenciDanisman)
Tabi bu örnek çok mantıklı olmadığı için performans açısından son 2 alternatifin tercih edilmesi daha doğru olacaktır.
JOIN seçeneklerini SELECT tümcesinde GROUP BY, HAVING, ORDER BY ile birlikte de kullanabiliriz. Aşağıdaki örnekte her danışmanın kaç öğrencisi olduğu sorgulanmıştır.
SELECT D.DanismanId, MAX(D.AdSoyad)[Danışmanın Adı Soyadı], COUNT(OD.OgrenciId) [Öğrenci Sayısı] FROM OgrenciDanisman OD INNER JOIN Danisman D ON OD.DanismanId=D.DanismanId GROUP BY D.DanismanId

Anti Join ve Semi Join
Join işlemleri genellikle iki tablo arasında eşleşen kayıtlar üzerinde birleştirme yapmak için kullanılır. Bazı durumlarda eşleşmeyen yani fark kayıtlar üzerinden de birleştirme yapılabilir. Bunun için Anti Join (Zıt Birleştirme) yöntemi kullanılır. Bu yöntem iki tablo arasındaki zıt kayıtları bulmaktır. Bunun için NOT IN ve NOT EXISTS sözcükleri kullanılır.
ÖrneÄŸin daha önce yaptığımız “danışmanı olmayan öğrenciler” örneÄŸini bu ÅŸekilde yapabiliriz.
SELECT * FROM Ogrenci O WHERE NOT EXISTS (SELECT * FROM OgrenciDanisman OD WHERE O.OgrenciId=OD.OgrenciId)

Bu örnekte iç kısımdaki OgrenciDanisman tablosu ile dış kısımdaki Ogrenci tablosu arasında içermeme iliÅŸkisi kurulmuÅŸtur. İki tablo arasındaki fark alınmıştır. Bu amaçla Oracle’de MINUS sözcüğü bulunur. SQL Server’de bu sözüğü karşılık ve içermeme iliÅŸkisi için EXCEPT ifadesi kullanılabilir.
SELECT OgrenciId FROM Ogrenci EXCEPT SELECT OgrenciId FROM OgrenciDanisman
Daha çok iki farklı veri setini birleştirme için kullanılan bakat bu sözüğün en büyük dezavantajı UNION ve INTERSECT sözcükleri gibi üst ve alt kayıt seti arasında doğrudan bir ilişki kuramaması ve her iki tarafta da kayıt kolon sayılarının eşit olmasını zorunlu kılmasıdır. Örneğin burada sözkonusu öğrencinin adını almak için ayrıca bir içiçe sorgu yazmak gerekiyor.
Konuyla ilgili bahsedeceğimiz diğer JOIN türü Zıt Birleştirmenin tersi olarak algılayabileceğimiz Semi-Join diye bilinen yarı birleştirme işlemidir. Burada IN ve EXISTS kullanılmaktadır. Önceki örneğin tersi olarak danışmanı olan öğrencileri listeleyelim.
SELECT * FROM Ogrenci O WHERE EXISTS (SELECT * FROM OgrenciDanisman OD WHERE O.OgrenciId=OD.OgrenciId)





Recent Comments