INNER, LEFT, RIGHT, CROSS (SQL Server JOIN Türleri – I)

SQL Server JOIN türleri kullanılır, INNER JOIN, OUTER JOIN (LEFT JOIN, RIGHT JOIN, CROSS JOIN) nedir?

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

  • 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)

INNER, LEFT, RIGHT, CROSS (SQL Server JOIN Türleri – I)” üzerine 26 düşünce

  1. ertugrul

    Sayın hocam test Projesi Yapacagım ASp.net Gridview checbox Kulanacagım nasıl Bir Mantıkla Yapa Bilirim Yardım Ederseniz Sevinir Yorumlarınız Aydınlatıcı Şimdiden Elinize Saglık Teşekurler
    Programlama Dilim C#, Veri Tabanım Sql

    Cevapla
  2. yavuz

    merhaba uyeler veri tabanı altında altı tablom var.bunları üçü ile veri listeleme yapmak istiyorum.üç tablomun isimleri

    (uyeler,resimler,arkadaslar). session ile gelen rumuz bilgisine bağlı olarak arkadaslar tablosundan sorgulama

    yaparak çıkan arkadas isimlerinin (rumuzlarının) resimler tablosundaki anaresim sütunundaki ismi ve uyeler

    tablosunda ki üye bilgilerini alarak listelemek istiyorum.(ekrana arkadaş rumuz bilgisini(arkadaslar tablosunda),

    anaresim bilgisini(resimler tablosundan), uye bilgilerini(uyeler tablosundan) listelemek istiyorum.)

    nasıl sorgu yazabilirim.(asp,access,sql kullanıyorum.)

    Cevapla
  3. Ahmet Kaymaz Yazar

    Merhaba Yavuz,

    aşağıdaki gibi bir sorgu yardımcı olabilir.

    SELECT U.Rumuz, A.Isım,
    FROM Uyeler U
    INNER JOIN Arkadaslar A ON U.Rumuz=A.Rumuz
    INNER JOIN Resimler R ON A.Rumuz=R.Rumuz

    Cevapla
  4. yavuz

    iyi akşamlar yanyana yazdığımda Microsoft OLE DB Provider for ODBC Drivers hata ‘80040e14’

    [Microsoft][ODBC Microsoft Access Sürücüsü] SELECT deyimi, saklý bir sözcük veya yanlýþ yazýlmýþ veya eksik bir baðýmsýz deðiþken içeriyor veya noktalama iþareti yanlýþ.

    /yeni-sitem/asp/arkadaslarim.asp, satır 135

    hatası nı veriyor. neden dir. yardımınıza şimdiden teşekkür ederim.

    Cevapla
  5. yavuz

    iyi akşamalr çok faydalandım. allah razı olsun.

    asp sql access kullanıyorum. hem resim1 kolonundan IS NOT NULL hemde onay1 kolonundan false/true birlikte nasıl sorgularım. (resim2 onay2 , resim3 onay3 , resim4 onay4 ) bunları da yapacağım.

    Cevapla
  6. osman kara

    Selamün Aleyküm hocam;
    Benim sorunum Bir tanlodaki null satırları filtreleyerek tablonun dolu stunlarını yine aynı tabloyu dolu çekmek.
    FULL JOIN veya FULL OUTER JOIN örneğinizdeki tabloda null değerler içeren satırlar var çeşitli kayıtlarda.

    i.hizliresim.com/E5bgOD.png

    Bu null değer içeren satırları filtreleyip nasıl alabilirim.ilgi ve alaka için şimdiden tşkler.

    Cevapla
  7. Osman kara

    Selamun Aleykum Hocam;

    anlatmak istediğim Slect * from TabloAdı sorgusunu çektiğimde tabloyu olduğu gibi çekmek fakat sorgu sonucu sadece dolu satır ve stunları yine tek parça tablo halinde şarta bağlı olmadan çekmek.

    Sadece dolu kayıtları tek tabloda şartsız çekmek isriyorum belli bir stun adıa bağlı olmadan..

    Cevapla
  8. Ahmet Kaymaz Yazar

    Osman,

    bir tabloda filte, koşul vermeden herhangi bir kısıtlama yapamazsın. Bazı satırlar gelmesin dediğin anda mutlaka şart vermek (WHERE) zorundasın.

    SELECT * FROM TabloAdi WHERE NOT Kolon1 IS NULL AND NOT Kolon2 IS NULL

    şeklinde içinde NULL olmayan tüm satırları getirebilirsin. Başka bir yolu bulunmamaktadır.

    Cevapla
  9. Ahmet Mehmet

    A/B U B/A nın birleşimini gösteren yani resimdeki sağ en alttaki resmin sorgusu yanlış olmuş, inner join yerine full outer join gerekmez mi?

    Cevapla
  10. Tuba

    Merhaba
    İzin tablom var izinlerinin start_date ve end_date’i var ben bu tarihler arası günleri tek tek günleri yazmak istiyorum.
    start_date : 01.01.2016 ve end_date : 03.01.2016
    A 01.01.2016
    A 02.01.2016
    A 03.01.2016
    yardımcı olursanız sevinrim

    Cevapla
  11. Ahmet Kaymaz Yazar

    Tuba,

    bunu birkaç şekilde oluşturabilirsin. Aşağıdaki 2 farklı yöntem bulabilirsin. 2016 Şubat ile Nisan arasındaki günleri listeliyor.

    SELECT DATEADD(DAY,number,’20160201′) Tarih
    FROM master..spt_values
    WHERE type = ‘P’
    AND DATEADD(DAY,number,’20160201′) < '20160430' declare @startDate date; declare @endDate date; select @startDate = '20160201'; select @endDate = '20160430'; with dateRange as ( select dt = dateadd(dd, 1, @startDate) where dateadd(dd, 1, @startDate) < @endDate union all select dateadd(dd, 1, dt) from dateRange where dateadd(dd, 1, dt) < @endDate ) select * from dateRange

    Cevapla
  12. hamit karakaya

    Merhaba Arkadaşlar.
    Benim join fonksiyonumda kayıtlar tekrar ediyor. Sorgu aşagıda ki gibidir;

    SELECT A_tablo.Personel_ID, A_tablo.Personel_Adsoyad, B_tablo.hakedis, C_tablo.Personel_izin
    FROM A_tablo LEFT OUTER JOIN
    B_tablo ON A_tablo.Personel_ID = B_tablo.Personel_ID LEFT OUTER JOIN
    C_tablo ON A_tablo.Personel_ID = C_tablo.Personel_ID

    Bu sorguda A_tablosunda deneme amaçlı sadece Personel_ID si 1 olan personel var ve B_tablosunda 6 ve C_tablosunda ID 1 olan 4 kayıt var.

    Sonuç olarak 6 satır içerisinde C_tablosundan 2 null değer ile dönmesini beklerken 24 değer dönüyor.
    B_tablosu her kayıt 4 defa, c_tablosu her kayıt 6 defa tekrarlanıyor.

    Yorumunuzu bekliyorum.
    Yanlışımı ve tekrar etmeyen kayıtları alabileceğim sorguyu yazabilir misiniz?

    İyi çalışmalar.

    Cevapla
  13. Ahmet Kaymaz Yazar

    Hamit,

    iki tabloyu JOIN yaptığımızda her iki tabloda uyuşanlar gelir. Eğer LEFT / RIGHT JOIN yapılmışsa karşı tabloda karşılığı olmayan kayıtlar için NULL döner.

    Personel_ID=1 için A tablosunda 1 kayıt ama B tablosunda 6 kayıt olduğu için A ve B’yi Personel_ID üzerinden join yaptığımızda 1×6 satır gelir. B ve C tablolarında kayıtları teke indirmen gerekiyor veya JOIN ifadesinde Personel_ID ile birlikte başka bir alanı da dahil etmek gerekiyor.

    Cevapla
    1. hamit karakaya

      HIZLI YANITINIZ İÇİN TEŞEKKÜR EDERİM,

      Başka bir sorguda A_tablosundaki her kolonu farklı bir tablo ile eşleştirip veri çekebiliyorum.
      Anlamlandıramadığım olay neden bir kolon verisi ile iki tabloyu eşleştirdiğimde çalışmaması?
      Yani benim mantığıma göre LEFT JOIN soldaki tablo kolonunu getir ve diğer tablolarda varsa yanına ekle.
      1 verisi için B tablosunda 3 veri varsa 3 satır olsun ve C tablosunda 4 veri varsa (önceki 3 + 1)4 satır olsun ve B’nin kolonuna bir adet “Null” veri eklensin???
      Biraz SQL çalışma mantığını burada kaçırıyorum.

      İyi çalışmalar.

      Cevapla
    2. hamit karakaya

      Merhaba,

      Belki standart personel ID anlatımım tam olmamış olabilir.
      A tablosundaki her ID için B ve C vs. tablolarında birçok liste tutuyorum. Yani A tablosundan seçilen bir değer programda B ve C için yeni listeleri çağırıyor. Tamamını tek seferde çekip kullanmam güncellemeleri kaçırmamıza sebep olabiliyor ve bu da karışıklığa sebep olduğundan online kullanmak zorundayım

      Bu tabloları farklı birkaç sorguyla network üzerinden göndererek alabilsem de programda küçük bekletmelere sebep oluyor, bu sebeple tek sorguda almaya çalışıyorum.

      İyi çalışmalar.

      Cevapla
      1. Ahmet Kaymaz Yazar

        Hamit merhaba,

        T1 ve T2 tablosunu JOIN yaptığımızda sadece eşleşen kayıtları getirmesi için INNER JOIN kullanılır. Fakat bir tablodaki tüm kayıtları getirmesini yani baskınlığı o tabloya verebilmesi için LEFT veya RIGHT JOIN kullanılır. Senin örneğinde de A, B ve C’yi aynı anda JOIN ettiğin için A’nin C’deki 4 alana karşılık B’de 4 kayıt olmadığından bu kayıt için B tarafında o alanlar NULL olacaktır.

        Cevapla
  14. Mesut ŞENEL

    Merhaba
    CROSS JOIN ile ekleme işlemi yapabilir miyiz. Mesela daha önceden eklemiş olduğumuz 1000 öğrenciye 5 adet dersi 1 ve 2. dönem olarak nasıl ekleyebiliriz.

    Cevapla
    1. Ahmet Kaymaz Yazar

      Mesut merhaba,

      CROSS JOIN doğrudan ekleme için kullanılan bir komut değildir. 1000 öğrenciyi söz konusu derslerle JOIN edip çıkan sonucu karşı tabloya INSERT edebilirsin.

      Cevapla

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Time limit is exhausted. Please reload CAPTCHA.