Veri dünyasına adım attığınızda karşınıza çıkan ilk ama en önemli kavramlardan biri ilişkilerdir, değil mi? Özellikle veritabanı tasarlarken ya da verileri anlamlandırmaya çalışırken bu ilişkiler hayat kurtarır. İşte bu noktada, veriler arasında kurduğumuz köprüler devreye giriyor. Bugün size, benim de zaman zaman takılıp kaldığım ama sonra ‘Ha, aslında buymuş!’ dediğim bir konudan, yani ‘Multi-Mapping: One-to-Many İlişkiler’den bahsedeceğim. Kulağa biraz teknik gelse de, inanın bana, günlük hayatımızdan örneklerle çok daha eğlenceli hale getirebiliriz bu konuyu.
Şöyle düşünün, bir restoran düşünün. Restoran sahibi tek kişidir ama o restorana gelen müşteriler birden fazladır. İşte bu, tam olarak ‘one-to-many’ yani ‘tekten çoka’ ilişkisinin canlı bir örneği. Tek bir ‘Restoran’ var, ama bu restorana bağlanan pek çok ‘Müşteri’ olabilir. Ya da bir üniversite düşünün; tek bir ‘Fakülte’ vardır, ama o fakülteye bağlı yüzlerce, binlerce ‘Öğrenci’ bulunur. Bu ilişkileri doğru kurmak, verilerinizi daha anlaşılır, daha yönetilebilir hale getirmenin anahtarıdır.
Peki, bu ‘one-to-many’ ilişkileri neden bu kadar önemli? Açıkçası, verileriniz arasında kaybolmamak için. Bir düşünün, elinizde bir sipariş listesi var. Bu listede her siparişin hangi müşteriye ait olduğunu bilmeniz gerekiyor. Eğer bu ilişkiyi doğru kuramazsanız, bir siparişin kimden geldiğini anlamak için saatlerce veri taramak zorunda kalırsınız. Bu da hem zaman kaybı hem de yanlış sonuçlara yol açabilir.
Şimdi gelelim işin biraz daha teknik ama bir o kadar da pratik kısmına. Bu ilişkileri veritabanlarında nasıl temsil ediyoruz? Genellikle ‘ilişkisel veritabanı’ dediğimiz sistemlerde, bir ana tablo ve ona bağlanan bir alt tablo kullanırız. Örneğin, ‘Müşteriler’ adında bir ana tablomuz var. Bu tabloda her müşterinin benzersiz bir ‘MüşteriID’si bulunur. Sonra ‘Siparişler’ adında bir alt tablo oluştururuz. Bu ‘Siparişler’ tablosunda da her siparişe ait bilgilerle birlikte, o siparişin hangi müşteriye ait olduğunu belirten bir ‘MüşteriID’ alanı bulunur. İşte bu ortak ‘MüşteriID’, iki tabloyu birbirine bağlayan köprü görevini görür.
Bu sayede, bir müşterinin verdiği tüm siparişleri tek bir sorguyla çekebiliriz. Ne güzel değil mi? Ya da tam tersi, belirli bir siparişin hangi müşteriye ait olduğunu da kolayca bulabiliriz. Bu, veri analizinde bize inanılmaz bir esneklik sağlar. Mesela, hangi müşterinin en çok sipariş verdiğini bulmak istediğinizde, bu ilişki sayesinde tek bir sorguyla sonuca ulaşırsınız. Kendi yazdığım bazı basit veri yönetimi araçlarında da bu mantığı hep kullanırım, çünkü işleri inanılmaz hızlandırıyor.
Bazen bu ‘one-to-many’ ilişkileri kurarken küçük hatalar yapabiliyoruz, bu da gayet normal. Örneğin, ‘MüşteriID’ alanını farklı tablolarda farklı isimlerle tanımlayabiliyoruz. Ya da ana tabloda olmayan bir ‘MüşteriID’yi alt tabloya ekleyerek veri bütünlüğünü bozabiliyoruz. Bu tür durumlar, verilerinize erişirken beklenmedik hatalara yol açabilir. Bu yüzden, bu ilişkileri kurarken dikkatli olmak ve anahtarları doğru tanımlamak çok önemli. Hani bazen bir program çalışmaz ya, sonra bakarsın ki bir anahtar eksik kalmış. İşte onun gibi bir şey.
Tabi, bu iş sadece veritabanlarıyla sınırlı değil. Yazılım geliştirirken de bu ‘one-to-many’ mantığını her yerde görürüz. Örneğin, bir kullanıcı profili düşünün. Tek bir ‘Kullanıcı’ bilgisi var ama bu kullanıcının birden çok ‘Yorum’u, ‘Mesajı’ veya ‘Takipçisi’ olabilir. Bu tür durumları yönetirken de yine aynı ilişki mantığını kullanırız.
Şimdi gelelim işin pratik kısmına, bir kod örneğiyle pekiştirelim. Basit bir C# örneği üzerinden, iki tablo arasındaki ‘one-to-many’ ilişkisini nasıl kurabileceğimizi görelim. Diyelim ki ‘Kategoriler’ ve ‘Ürünler’ adında iki tablomuz var. Bir kategorinin birden çok ürünü olabilir, ama bir ürün yalnızca tek bir kategoriye aittir. Bu ilişkiyi Dapper ile nasıl yapabiliriz, bir bakalım.
Öncelikle, veri modellerimizi tanımlayalım. Basit tutalım:
public class Kategori { public int KategoriID { get; set; } public string KategoriAdi { get; set; } public List Urunler { get; set; } // One-to-many ilişkisi burada! }public class Urun { public int UrunID { get; set; } public string UrunAdi { get; set; } public int KategoriID { get; set; } // Foreign Key public decimal Fiyat { get; set; } }
Şimdi de bu iki tabloyu birbirine bağlayan ve ilişkili veriyi çeken sorgumuzu yazalım. Genellikle bu tür senaryolarda, ana tabloyu çektikten sonra alt tablodaki ilgili kayıtları da getirmenin birkaç yolu var. En yaygın olanı, iki ayrı sorgu çekip sonra bellekte birleştirmektir. Ama daha performanslı bir yol da ‘JOIN’ kullanarak tek seferde çekmektir. Dapper ile bunu şöyle yapabiliriz:
// Bu sadece bir örnek, tam bir Dapper implementasyonu değil // Gerçek uygulamada connection string ve sorgu düzenlemesi gerektirir.// Önce tüm kategorileri çekelim var kategoriler = await connection.QueryAsync( @"SELECT K.*, U.* FROM Kategoriler K LEFT JOIN Urunler U ON K.KategoriID = U.KategoriID", (kategori, urun) => { if (kategori.Urunler == null) kategori.Urunler = new List(); if (urun != null) kategori.Urunler.Add(urun); return kategori; }, splitOn: "KategoriID,UrunID"); // Hangi sütunlardan sonra yeni tabloya geçileceğini belirtiyoruz
// Şimdi kategoriler listesi, içindeki Urunler ile birlikte dolu olacak. // Fakat aynı kategoriden birden fazla kez çekilme ihtimali var. // Bu yüzden son bir işlemle birleştirmemiz gerekiyor.
var sonuc = kategoriler.GroupBy(k => k.KategoriID).Select(g => { var kategori = g.First(); kategori.Urunler = g.Select(item => item).Where(item => item.UrunID != 0).ToList(); // UrunID 0 ise, boş bir kategori gelmiş demektir. return kategori; }).ToList();
// Artık 'sonuc' listesinde her Kategori nesnesinin içinde, ona ait Urunler listesi dolu olacak. // Sanırım bu kadar basit :)
Bu kodda, `QueryAsync` metoduyla hem ‘Kategoriler’ hem de ‘Urunler’ tablosundan verileri çektik. `splitOn` parametresi, Dapper’a hangi sütundan sonra yeni bir tablo modeline geçmesi gerektiğini söylüyor. Ardından, gelen kayıtları `GroupBy` ile birleştirerek her kategoriye ait ürünleri `List
Neticede, ‘one-to-many’ ilişkileri veri modellemesinin temel taşlarından biri. İster bir veritabanı tasarlıyor olun, ister bir uygulama geliştiriyor olun, bu ilişki tipini doğru anlamak ve uygulamak, kodunuzun daha temiz, daha performanslı ve daha anlaşılır olmasını sağlar. Yani şey gibi, bir yapbozun parçalarını doğru yerlere yerleştirmek gibi. Yanlış yerleştirirseniz resim tamamlanmaz, doğru yerleştirirseniz harika bir bütün oluşur.
Umarım bu açıklama, ‘multi-mapping’ ve ‘one-to-many’ ilişkileri konusundaki kafa karışıklığını biraz olsun gidermiştir. Gerçekten de verilerle çalışırken bu temel yapıları sağlam kurmak, ileride yaşayacağınız pek çok sorunun önüne geçer. Açıkçası ben de zaman zaman bu tür konulara geri dönüp kendimi tazelerim. Çünkü teknoloji sürekli ilerliyor ama bu temel mantıklar hep baki kalıyor. Ne güzel değil mi?
Bu arada, bu tür ilişkileri daha detaylı incelemek isterseniz, Google’da “one-to-many relationship database” diye aratabilirsiniz. Karşınıza çıkan sonuçlar size daha da derinlemesine bilgi verecektir. Hatta YouTube’da da bolca görsel anlatım bulabilirsiniz, buradan bir göz atabilirsiniz.
Sonuç olarak, verileriniz arasındaki bu ‘tekten çoka’ bağlantıları kurarken sabırlı ve dikkatli olun. Doğru kurulmuş bir ilişki, veritabanınızın can damarıdır. Tıpkı benim dağcılık yaparken kurduğum emniyet kemeri gibi, sağlam olmalı ki güvenle ilerleyebilin. 🙂