Veritabanı kurgusu iki şekilde dizayn edilir;
Birincisi allah ne verdiyse önüne, sağına, soluna bakmadan yani kurgusuz ve tembellik sonucu ortaya çıkan… İkincisi ise daha temkinli, önünü ve arkasını gören, “Ne olur ne olmaz bunu da koyalım” veya “Bunu böyle yaparsak ilerde mıçarız” önlemler dizisini kavrayan, yani “Tuvalete oturmadan önce musluğunu kontrol et bence” deyimini benimsemiş olarak dizayn edendir.
Bunları şu sebepten söylüyoruz. Günümüzde hala tablolarda boş yere veri tekrarı yapan, düzgün bir başlangıç yapmayan, bazen ise ilişkilendirme işlemlerini abartan yapılandırmalar var. Bu sebepten dolayı JOIN işlemlerinin gerekliliğini önceden iyi tespit edip veritabanı kurgusunu ona göre yapmak gerekmektedir. Bazen aşırı JOIN anahtarına tabi sorgular performans kayıplarına da yol açabilmektedir.
Aşağıdaki örnek bloklar, bu durumu çözen örneklemeler değildir fakat en azından ön ayak olur diye tahmin ediyorum.
Bir de bir kısım örneklerde A tablosundaki B kolonu şeklindeki saçmalıklar öğrenime katkının tersine daha çok okunduktan sonra sanki ot içmiş Süleyman abi hissine kaptırır. Bunun yerine direk canlı ve gerçek hayatta da kullanılan örneklemelerde bulunacağım.
Join’e dalmadan önce ilişkili tablolar nasıl olur bi bakalım. Önce egzampıl tablolarımızı oluşturalım. Özellikle tablolara fazla kolon eklemekten kaçındım yoksa kafa bulandıracaktır.
--
-- Yazilar
--
CREATE TABLE `article` (
`id` int(11) NOT NULL auto_increment,
`title` varchar(64) NOT NULL,
`text` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Yazilara yapilan yorumlar
--
CREATE TABLE `article_comment` (
`id` int(11) NOT NULL auto_increment,
`member_id` int(11) NOT NULL,
`article_id` int(11) NOT NULL,
`message` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Yazilara yapilan yorumlari begenenler
--
CREATE TABLE `article_comment_liked` (
`id` int(11) NOT NULL auto_increment,
`member_id` int(11) NOT NULL,
`article_comment_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Uyeler
--
CREATE TABLE `member` (
`id` int(11) NOT NULL auto_increment,
`firstname` varchar(64) NOT NULL,
`lastname` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;Tablolar tamamdır. Yazının devamındaki örneklerde kullanmak için içlerini aşağıdaki kod bloğunu kullarak dolduralım. Bir de script içerisindeki comment çekilmiş bölümleri de okumanızı tavsiye ederim.
--
-- üye tablosuna bişeyler ekle ama sentez olsun.
-- id ler auto increment ama ben örneklerde rahat kullanabilmek için elle veriyorum. 100 serisi üyeler için gelsin
--
INSERT INTO `member` VALUES
('101', 'Hakkı', 'Bulut'),
('102', 'Bülent', 'Ersoy'),
('103', 'Gevrek', 'Abi'),
('104', 'Tiki', 'Bacı'),
('105', 'Osmanların', 'Kiracı'),
('106', 'Sıyrık', 'Gacı'),
('107', 'Suzan', 'Hacı');
--
-- yazı tablosuna bişeyler ekle
-- id ler auto increment ama ben örneklerde rahat kullanabilmek için elle veriyorum. 200 serisi yazılar için gelsin.
--
INSERT INTO `article` VALUES
('201', 'Başlık Bir', 'İçerik Bir'),
('202', 'Başlık İki', 'İçerik İki'),
('203', 'Başlık Üç', 'İçerik Üç'),
('204', 'Başlık Dört', 'İçerik Dört'),
('205', 'Başlık Beş', 'İçerik Beş'),
('206', 'Başlık Altı', 'İçerik Altı');
--
-- yorum tablosuna bişeyler ekle
-- id ler auto increment ama ben örneklerde rahat kullanabilmek için elle veriyorum. 300 serisi begenenler için gelsin.
--
INSERT INTO `article_comment` VALUES
('301', '101', '201', 'Olmamış.'),
('302', '102', '201', 'Olmuş olmuş sadece makamı acem kürdi yap evladım.'),
('303', '101', '202', 'Tebirkler güzel yazı.'),
('304', '103', '203', 'Ayyy fecii-bokta paylaştım bunu. Çok güzeeeel.'),
('305', '104', '203', 'Kızım düzgün yaz o nasıl Türkçe. Yazı güzel bu arada kardeşim.'),
('306', '105', '204', 'çok iyi de oldu çok güzel iyi oldu taam mı?'),
('307', '103', '207', 'bu yorum böyle bir blogda nasıl yayınlandı. Savcıların harekete geçmesi gerek.');
--
-- yorumları begenenler tablosuna bişeyler ekle
-- ID'ler otomatik olmasına karşın ben manuel ekleme yaptım. Sıkıntı yok devam.
--
INSERT INTO `article_comment_liked` VALUES
('1', '101', '301'),
('2', '101', '302'),
('3', '102', '301'),
('4', '103', '304'),
('5', '103', '305'),
('6', '103', '306'),
('7', '104', '306'),
('8', '105', '302'),
('9', '106', '301'),
('10', '106', '303'),
('11', '107', '304');Tablolarda doldu. O zaman asıl meseleye gelmeden son olarak JOIN’leri bilelim.
INNER JOIN
INNER JOIN, ilişkilendirildiği diğer tabloyla bire bir bütünlük sağlayarak sonuç döner. Yani INNER JOIN’inde belirtilen ON koşulundaki kolonun her iki tabloda da eşitlik sağlaması kesinliği esasına dayanır. Sadece JOIN anahtarının kullanılması INNER JOIN ile aynıdır. Kafa bulandıysa aşağıdaki örnekler netleştirecektir.
LEFT [OUTER] JOIN
LEFT JOIN, ilişkilendirildiği diğer tabloyla bire çok bütünlük sağlayarak sonuç döner. Yani LEFT JOIN’inde belirtilen tablonun solundaki tablonun koşula uysada uymasa da tüm verilerini getir anlamındadır.
RIGHT [OUTER] JOIN
RIGHT JOIN de tıpkı LEFT JOIN’deki gibi ilişkilendirildiği diğer tabloyla bire çok veri sonucu döner. LEFT JOIN’inden bir farkı yoktur desem yeridir. Sadece tabloyu sorgudaki konumlandırdığınız yer itibariyle ihtiyaç duyulur. Demek oluyorki tablonun sağındaki tüm verileri koşula uysada uymasa da getir.
İlk önce ne yapmak istediğimizi bilelim ve istediklerimizi sırayla sorgulayalım;
201 ID’li yazıya yapılan yorumlar ve yorumların sahiplerini getir. (INNER JOIN)
Bu sorguda yorum tablosunun üye ID’leriyle üye tablosunun üye ID’leri birleştiriliyor. Bunun yanında bir de yorum tablosundaki “201 ID’li olan yazıların yorumlarını getir” koşulu ile filterelemiş bulunuyoruz. Ve görüyoruz ki JOIN sayesinde üye tablosundaki yorum sahiplerinin isimlerini de yorumun hemen yanında getirebiliyoruz.
-- 201 ID'li yazıya yapılan yorumlar ve yorumların sahiplerini getir.
SELECT
ac.message,
m.firstname,
m.lastname
FROM
article_comment as ac
INNER JOIN
member as m
ON
ac.member_id = m.id
WHERE
ac.article_id = 201Sadece yorum yapılmış tüm yazıları getir. (INNER JOIN)
Burada GROUP BY anahtarını kullanmadığımız zaman birleştirmeden dolayı yorum yapılmış yazılarda bir tekrar söz konusu olacaktır. Bu sebepten bunu sorguya iliştirdim. Kaldırıp sonucunu görebilirsiniz.
-- Sadece yorum yapılmış tüm yazıları getir.
SELECT
a.*
FROM
article as a
INNER JOIN
article_comment as ac
ON
a.id = ac.article_id
GROUP BY ac.article_idYorumları like edilmiş (beğenilmiş) yazıları getir. (INNER JOIN)
Bu sorguda görüldüğü üzere iki JOIN var. Sebebi ise başlıkta da belirttiğim gibi YORUMLARI LIKE edilmiş YAZILARI getir. Yani işin içinde artık 3 tablo var. Sorgu sonucunda istediğimiz verinin tablosunu FROM’a yazdıktan sonra kalan kısmı diğer birleştirmeler…
-- Yorumları like edilmiş (beğenilmiş) yazıları getir.
SELECT
a.*
FROM
article as a
INNER JOIN
article_comment as ac
ON
ac.article_id = a.id
INNER JOIN
article_comment_liked as acl
ON
acl.article_comment_id = ac.id
GROUP BY ac.article_idYorum yapılmış yazılarla birlikte yorum yapılmamış yazıları da getir. (LEFT [OUTER] JOIN)
Bu sorgu tüm yorum yapılmış yazıları getirecek fakat arada yorum yapılmamış yazıların da gelmesini istiyoruz. Bunun için LEFT JOIN kullanmak gerektiğini biliyoruz ve son olarak şunu diyoruz. Sen git bu anahtarın solundaki tabloda ne kadar veri varsa koşula uymasanda getir. Aşağıdaki sorgu sonucunda da gördüğünüz gibi tüm yazılar geldiğinden bu yazılara karşılık gelen yorumlar olmadığından NULL döndürmektedir.
-- Yorum yapılmış yazılarla birlikte yorum yapılmamış yazıları da getir.
SELECT
a.id,
a.title,
ac.article_id,
ac.message
FROM
article as a
LEFT JOIN
article_comment as ac
ON
ac.article_id = a.idTüm yazılara yapılmış yorumları yazı var olmasa da getir. (RIGHT [OUTER] JOIN)
Bu sorgu tüm yazılara yapılmış yorumları getirecek fakat yazılardan birisi küfürlü olduğundan silinmiş ve o yazıya yapılan yorum da duruyor. Normalde LEFT JOIN anahtarında soldaki tablonun tüm verilerinin gelmesi kuralı vardı. RIGHT JOIN’de ise sağ taraftaki tablonun koşula uymasa da tüm verilerini getir kuralı söz konusu. Bu sorgu sonucunda önceden küfür dolayısıyla silinmiş bir yazıya yapılan yorum da gelecektir. Fakat yazı NULL olarak dönecektir.
-- Tüm yazılara yapılmış yorumları yazı var olmasada getir.
SELECT
a.id,
a.title,
ac.message
FROM
article as a
RIGHT JOIN
article_comment as ac
ON
ac.article_id = a.idBu örneklemeler bu yapı için daha da çoğaltılabilir. Sadece ne istediğimizi önceden bilelim ve kurguyu ona göre yapalım. Yoksa bir zaman sonra tüm yapıyı çöpe atmak gibi bir derdiniz olmasın.




6 Yorum
28 Ağustos 2010 - 15:59
Çok güzel bir yazı olmuş dostum. Niye gerçek hayattan örnek verdin ki? Biz soyut örneklerle gayet iyi anlıyorduk =)
29 Ağustos 2010 - 00:23
Sonuna ifade vermesen yanlış anlaşılacak
Mahfetti bizi bu hayvanlar alemi ve futbolcu örnekleri kardeşim. Ahdım var kurban bayramında örnekleri verilen hayvanları tespit edip, satın alıp, kesip garibanlara dağıtıcam. Gerçi hayvanların etlerinde 1 ve 0′lar geziniyo olacak yapcak bişey yok 
31 Ağustos 2010 - 18:01
Ayrıntı yazın için teşekkürler. Örnekler de gayet açıklayıcı olmuş.
05 Temmuz 2011 - 12:51
şimdi benim merak ettiğim konu şu. join ile tabloları yanyana getirmek istiyorum ama soldaki tablodaki 1 kayıta karşılık sağdaki tablodan sadece benim istediğim kriterlere uyan sadece ilk satır gelsin istiyorum bunu nasıl yaparım
not sağdaki tabloya birden fazla koşul sağlanmakta ve birden fazla sütün gelmekte
yardımlarınız için şimdiden teşekkürler.
06 Temmuz 2011 - 15:13
Öncelikle ilgin için teşekkür ederiz. Burada kullanman gereken join tipini belirlemen gerekir. Şöyle ki;
Sorgu sonucunda sağ tablodan gelen veriler sol tablodaki kolona mutlak bağlıysa ve gelmesi zorunluysa INNER olmalı. Eğer zorunluluk yoksa LEFT kullanılarak olmayan verilerinde gelmesi sağlanabilir. Sen nokta atışı ile veri bulmak istiyorsan INNER ile başlayarak aşağıdaki ON koşulundaki gibi ekstra koşul belirtebilirsin. Böylece belirlediğin bu kriterler sağ tablodan istediğin verileri getirecektir.
Aşağıdaki örnek makaledeki ilk sorgunun güncellenmiş halidir.
Bu sorgu yazılara yorum yazmış üyeleri getirir. Ekstra koşul ile sadece ’101′ ID’sine sahip üyeyi getirecektir.
————————————-
SELECTac.message,
m.firstname,
m.lastname
FROM
article_comment as ac
INNER JOIN
member as m
ON
ac.member_id = m.id AND
m.id = 101
04 Ocak 2012 - 16:32
ben örnekle daha net anlatayım bir eticaret sistemi
bir tabloda ürünler diğer tabloda ürünlere ait fiyatlar olsun
birinci tablodan 12 adet ürün 2. tablodan ise her ürüne sadece 1 fiyat denk gelecek şekilde ve fiyat belirtilen tarihler arasında geçerli olacak inner join ile 2.tabloda birden fazla fiyat olduğundan duplicate oluyor benim istediğim 1 ürüne alt sorguda birden fazla alan ve birden fazla kriter ile sorgulama ve ayrıca alt sorguda yine order kullanımı bunun ile ilgili bir örnek verebilirmisiniz acaba ??