Bir veritabanında kalıtımı etkili bir şekilde nasıl modellersiniz?


131

Veritabanlarında kalıtımı modellemeye yönelik en iyi uygulamalar nelerdir?

Ödünleşimler nelerdir (örneğin sorgulanabilirlik)?

(En çok SQL Server ve .NET ile ilgileniyorum, ancak diğer platformların bu sorunu nasıl ele aldığını da anlamak istiyorum.)


14
"En iyi uygulama" ile ilgileniyorsanız, yanıtların çoğu yanlıştır. En iyi uygulama, RDb'nin ve uygulamanın bağımsız olduğunu belirtir; tamamen farklı tasarım kriterlerine sahiptirler. Bu nedenle, bir veritabanında "kalıtımı modellemek" (veya RDb'yi tek bir uygulama veya uygulama diline uyacak şekilde modellemek) çok kötü bir uygulamadır, bilgisizdir ve temel RDb tasarım kurallarını ihlal eder ve onu etkisiz hale getirir.
PerformanceDBA


6
@PerformanceDBA Peki, DB modelinde kalıtımdan kaçınmak için öneriniz nedir? Diyelim ki 50 farklı türde öğretmenimiz var ve bu öğretmeni sınıfa bağlamak istiyoruz. Miras olmadan bunu nasıl başarırsınız?
svlada

1
@svlada. Bu, bir RDb'de uygulanması basittir, bu nedenle "miras" gereklidir. Bir soru sorun, tablo tanımlarını ve bir örneği ekleyin, ben de detaylı olarak cevaplayayım. OO şartlarında yaparsanız, kraliyet karmaşası olur.
PerformanceDBA

Yanıtlar:


162

Bir veritabanında kalıtımı modellemenin birkaç yolu vardır. Hangisini seçeceğiniz ihtiyaçlarınıza bağlıdır. İşte birkaç seçenek:

Tür Başına Tablo (TPT)

Her sınıfın kendi tablosu vardır. Temel sınıf, içinde tüm temel sınıf öğelerine sahiptir ve ondan türetilen her sınıfın, aynı zamanda temel sınıf tablosunun yabancı anahtarı olan bir birincil anahtara sahip kendi tablosu vardır; türetilmiş tablonun sınıfı yalnızca farklı öğeleri içerir.

Yani mesela:

class Person {
    public int ID;
    public string FirstName;
    public string LastName;
}

class Employee : Person {
    public DateTime StartDate;
}

Aşağıdaki gibi tablolarla sonuçlanır:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK, FK)
datetime startdate

Hiyerarşi Başına Tablo (TPH)

Tüm kalıtım hiyerarşisini temsil eden tek bir tablo vardır, bu da birkaç sütunun muhtemelen seyrek olacağı anlamına gelir. Sisteme bunun ne tür bir satır olduğunu söyleyen bir ayırıcı sütun eklenir.

Yukarıdaki sınıflar göz önüne alındığında, bu tabloyla sonuçlanırsınız:

table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate

Satır türü 0 (Kişi) olan tüm satırlar için başlangıç ​​tarihi her zaman boş olacaktır.

Beton Başına Tablo (TPC)

Her sınıfın, diğer tablolara referans olmadan kendi tamamen oluşturulmuş tablosu vardır.

Yukarıdaki sınıflar göz önüne alındığında, şu tablolarla sonuçlanırsınız:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate

23
'Hangisini seçeceğiniz ihtiyaçlarınıza bağlıdır' - sorunun özünü seçimler için nedenlerin oluşturduğunu düşündüğüm için lütfen detaylandırın.
Alex

12
Soru hakkındaki yorumuma bakın. Mevcut Rdb teknik terimleri için komik yeni isimler kullanmak kafa karışıklığına yol açar. "TPT", süper tür alt türdür. "TPH" Normal Değildir, büyük bir hata. "TPH" daha da az Normalleştirilmiş, başka bir büyük hata.
PerformanceDBA

45
Yalnızca bir DBA, normalleştirmenin her zaman bir hata olduğunu varsayabilir. :)
Brad Wilson

7
Denormalizasyonun bazı durumlarda performans kazanımları ile sonuçlandığını kabul etmekle birlikte, bu tamamen DBMS'deki verilerin mantıksal ve fiziksel yapısı arasındaki eksik (veya var olmayan) bir ayrımdan kaynaklanmaktadır. Ne yazık ki ticari DBMS'nin çoğu bu sorundan muzdariptir. @PerformanceDBA doğru. Yetersiz normalleştirme, hız için veri tutarlılığından ödün veren bir yargı hatasıdır. Ne yazık ki, DBMS düzgün tasarlanmış olsaydı bir DBA veya geliştiricinin asla yapması gerekmeyecek bir seçim. Kayıt için ben bir DBA değilim.
Kenneth Cochran

6
@Brad Wilson. Yalnızca bir geliştirici, "performans için" veya başka bir şekilde normalleştirebilir. Çoğu zaman bu normalleşme değildir, gerçek şu ki normalleştirilmemiştir. Normalizasyonun kaldırılması ya da normalleştirilmesinin bir hata olduğu, teorinin desteklediği ve milyonlarca kişinin deneyimlediği bir gerçektir, bu bir "varsayım" değildir.
PerformanceDBA

133

Doğru veritabanı tasarımı, doğru nesne tasarımı gibi bir şey değildir.

Veritabanını basitçe nesnelerinizi serileştirmek dışında başka bir amaçla kullanmayı planlıyorsanız (raporlar, sorgulama, çoklu uygulama kullanımı, iş zekası vb.), O zaman nesnelerden tablolara herhangi bir basit eşleştirme önermem.

Pek çok insan bir veritabanı tablosundaki bir satırı bir varlık olarak düşünür (bu terimlerle uzun yıllar boyunca düşündüm), ancak bir satır bir varlık değildir. Bu bir öneridir. Bir veritabanı ilişkisi (yani tablo), dünya hakkında bazı gerçekleri ifade eder. Satırın varlığı gerçeğin doğru olduğunu gösterir (ve tersine, yokluğu gerçeğin yanlış olduğunu gösterir).

Bu anlayışla, nesne yönelimli bir programdaki tek bir türün bir düzine farklı ilişkide saklanabileceğini görebilirsiniz. Ve çeşitli türler (kalıtım, ilişkilendirme, birleştirme ile birleştirilmiş veya tamamen bağımsız) tek bir ilişkide kısmen depolanabilir.

Kendinize, hangi gerçekleri saklamak istediğinizi, hangi soruların yanıtlarını isteyeceğinizi, hangi raporları oluşturmak istediğinizi sormanız en iyisidir.

Uygun DB tasarımı oluşturulduktan sonra, nesnelerinizi bu ilişkilere göre serileştirmenize izin veren sorgular / görünümler oluşturmak basit bir konudur.

Misal:

Bir otel rezervasyon sisteminde, Jane Doe'nin Seaview Inn'de 10-12 Nisan tarihlerinde bir oda için rezervasyonu olduğu gerçeğini kaydetmeniz gerekebilir. Bu, müşteri varlığının bir özelliği mi? Otel varlığının bir niteliği mi? Müşteri ve oteli içeren mülklere sahip bir rezervasyon işletmesi mi? Nesne yönelimli bir sistemdeki bunlardan herhangi biri veya tümü olabilir. Bir veritabanında, bunların hiçbiri değildir. Bu sadece çıplak bir gerçektir.

Farkı görmek için aşağıdaki iki sorguyu düşünün. (1) Jane Doe'nin gelecek yıl için kaç otel rezervasyonu var? (2) Seaview Inn'de 10 Nisan için kaç oda rezerve edildi?

Nesneye yönelik bir sistemde, sorgu (1) müşteri varlığının bir niteliğidir ve sorgu (2), otel varlığının bir özniteliğidir. Bunlar, bu özellikleri API'lerinde açığa çıkaracak nesnelerdir. (Yine de, açıkça bu değerlerin elde edildiği iç mekanizmalar diğer nesnelere referansları içerebilir.)

İlişkisel bir veritabanı sisteminde, her iki sorgu da sayılarını almak için rezervasyon ilişkisini inceler ve kavramsal olarak başka herhangi bir "varlık" ile uğraşmaya gerek yoktur.

Bu nedenle, uygun bir ilişkisel veri tabanının yapılandırılması, varlıkları niteliklerle depolamaya çalışmak yerine, dünya hakkındaki gerçekleri saklamaya çalışmaktır. Düzgün bir şekilde tasarlandıktan sonra, tasarım aşamasında hayal bile edilemeyen faydalı sorgular kolayca oluşturulabilir, çünkü bu sorguları yerine getirmek için gereken tüm gerçekler uygun yerlerinde.


12
+1 Sonunda, cehalet denizinde gerçek bir bilgi adası (ve onların sınırları dışında bir şey öğrenmeyi reddetme). Kabul edildi, bu sihir değil: RDb, RDb ilkeleri kullanılarak tasarlandıysa, herhangi bir "sınıfı" "eşlemek" veya "projelendirmek" zahmetsizdir. RDb'yi sınıf temelli gereksinimlere zorlamak tamamen yanlıştır.
PerformanceDBA

2
İlginç cevap. Kabul edilen cevapta Kişi-Çalışan örneğini modellemeyi nasıl önerirsiniz?
sevenforce

2
@ sevenforce-DB tasarımı, gerçekten verilmeyen sistem gereksinimlerine bağlıdır. Karar vermek için neredeyse yeterli bilgi yok. Pek çok durumda, "tip başına masa" tasarımına benzer bir şey, körü körüne takip edilmediği takdirde uygun olabilir. Örneğin, başlangıç ​​tarihi muhtemelen bir Çalışan nesnesinin sahip olması için iyi bir özelliktir, ancak bir kişi birden çok başlangıç ​​tarihiyle birden çok kez işe alınabileceğinden, veritabanında gerçekten İstihdam tablosundaki bir alan olmalıdır. Bu, nesneler için (en yenisini kullanacak olan) önemli değildir, ancak veritabanında önemlidir.
Jeffrey L Whitledge

2
Elbette, sorum esas olarak kalıtımı modellemenin yolu ile ilgiliydi. Yeterince net olmadığım için özür dilerim. Teşekkürler. Bahsettiğiniz gibi, büyük olasılıkla Employmenttüm istihdamları başlangıç ​​tarihleri ​​ile toplayan bir tablo olmalıdır . Öyleyse, bir'nin mevcut işe başlama tarihini bilmek Employerönemliyse, bu View, sorgulama yoluyla mülkü içeren a için uygun bir kullanım durumu olabilir mi? (not: takma
adımdan

5
Bu gerçek bir cevabın cevheridir. Gerçekten işe koyulmak için biraz zamana ihtiyacı olacak ve doğru yapmak için biraz alıştırma gerektirecek, ancak bu, ilişkisel veritabanı tasarımındaki düşünce sürecimi zaten etkiledi.
MarioDS

9

Kısa cevap: yapmıyorsun.

Nesnelerinizi serileştirmeniz gerekiyorsa, bir ORM kullanın veya daha iyisi aktif kayıt veya prevaylence gibi bir şey kullanın.

Verileri depolamanız gerekiyorsa, onu ilişkisel bir şekilde saklayın (ne sakladığınız konusunda dikkatli olun ve Jeffrey L Whitledge'ın az önce söylediklerine dikkat edin), nesne tasarımınızdan etkilenen biri değil.


3
+1 Bir veritabanında kalıtımı modellemeye çalışmak, iyi ve ilişkisel kaynakların israfıdır.
Daniel Spiewak

7

Brad Wilson'ın da bahsettiği gibi TPT, TPH ve TPC kalıpları gittiğiniz yollardır. Ama birkaç not:

  • Bir temel sınıftan miras alan alt sınıflar, veritabanındaki temel sınıf tanımına göre zayıf varlıklar olarak görülebilir, yani temel sınıflarına bağımlıdırlar ve onsuz var olamazlar. Her bir alt tablo için benzersiz kimliklerin depolandığını ve aynı zamanda FK'yi üst tabloda tuttuğunu birkaç kez gördüm. Bir FK yeterlidir ve alt ve temel tablolar arasındaki FK-ilişkisini silmek için kademeli olarak etkinleştirmek daha da iyidir.

  • TPT'de, yalnızca temel tablo kayıtlarını görerek, kaydın hangi alt sınıfı temsil ettiğini bulamazsınız. Bu bazen, tüm kayıtların bir listesini yüklemek istediğinizde gereklidir ( select her alt tabloda yapmadan ). Bunu halletmenin bir yolu, alt sınıfın türünü temsil eden bir sütuna sahip olmaktır (TPH'deki rowType alanına benzer), böylece TPT ve TPH'yi bir şekilde karıştırın.

Aşağıdaki şekil sınıfı diyagramını içeren bir veritabanı tasarlamak istediğimizi varsayalım:

public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}

public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}

public class Circle : Shape {
Point center;
int radius;
}

Yukarıdaki sınıflar için veritabanı tasarımı şu şekilde olabilir:

table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)

table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;

table Circle
----------
int ShapeID; (FK on delete cascade)  
int centerX;
int center;
int radius;

4

Bir DB'de kurabileceğiniz iki ana kalıtım türü vardır, her varlık için tablo ve Hiyerarşi başına tablo.

Varlık başına tablo, tüm alt sınıfların paylaşılan özelliklerine sahip bir temel varlık tablonuzun olduğu yerdir. Daha sonra, çocuk sınıfa göre her biri yalnızca o sınıfa uygulanabilir özelliklere sahip başka bir tablonuz olur. PK'leri ile 1: 1 bağlantılıdırlar

alternatif metin

Hiyerarşi başına tablo, tüm sınıfların bir tablo paylaştığı ve isteğe bağlı özelliklerin null yapılabilir olduğu yerdir. Ayrıca, kaydın şu anda tuttuğu türü belirten bir sayı olan bir ayırt edici alandır.

alternatif metin SessionTypeID ayırıcıdır

Hiyerarşi başına hedefi sorgulamak daha hızlıdır, çünkü birleştirmelere ihtiyacınız yoktur (yalnızca ayırıcı değer), oysa varlık başına hedef, bir şeyin ne tür olduğunu saptamak ve tüm verilerini geri çekmek için karmaşık birleştirmeler yapmanız gerekir.

Düzenleme: Burada gösterdiğim görüntüler, üzerinde çalıştığım bir projenin ekran görüntüleridir. Varlık görüntüsü tam değildir, dolayısıyla boştur, ancak esas olarak tablolarınızın içine ne koyacağınızı değil, nasıl kurulduğunu göstermekti. Bu sana bağlı ;). Oturum tablosu, Sanal işbirliği oturumu bilgilerini içerir ve ne tür bir işbirliğinin dahil olduğuna bağlı olarak birkaç oturum türü olabilir.


Ayrıca Beton Başına Hedef sınıfının kalıtımı gerçekten iyi modellemediğini düşünürdüm ve bu yüzden göstermedim.
mattlant

Resmin nereden geldiğine dair bir referans ekleyebilir misiniz?
chryss

Cevabınızın sonunda bahsettiğiniz resimler nerede?
Musa Haidari

1

Veritabanınızı normalleştirirsiniz ve bu aslında mirasınızı yansıtır. Performans düşüşü olabilir, ancak normalleşmede durum böyledir. Dengeyi bulmak için muhtemelen sağduyulu olmanız gerekecek.


2
İnsanlar neden bir veritabanını normalleştirmenin performansı düşürdüğüne inanıyor? İnsanlar ayrıca DRY ilkesinin kod performansını düşürdüğünü düşünüyor mu? bu yanlış algılama nereden geliyor?
Steven A.Lowe

1
Muhtemelen normalden arındırma performansı artırabildiğinden, görece konuşmak gerekirse normalleştirme performansı düşürür. Kabul ettiğimi söyleyemem, ama muhtemelen böyle ortaya çıktı.
Matthew Scharley

2
Başlangıçta, normalizasyonun performans üzerinde küçük bir etkisi olabilir, ancak zamanla, satır sayısı arttıkça, verimli JOIN'ler daha büyük tablolardan daha iyi performans göstermeye başlayacaktır. Elbette, normalleşmenin başka, daha büyük faydaları vardır - tutarlılık ve fazlalık eksikliği, vb.
Rob

1

benzer ileti dizisinin tekrarı

VEYA eşlemede, miras, üst ve alt tabloların aynı tanımlayıcıyı kullandığı bir üst tabloyla eşleşir

Örneğin

create table Object (
    Id int NOT NULL --primary key, auto-increment
    Name varchar(32)
)
create table SubObject (
    Id int NOT NULL  --primary key and also foreign key to Object
    Description varchar(32)
)

SubObject, Object ile yabancı anahtar ilişkisine sahiptir. Bir SubObject satırı oluşturduğunuzda, önce bir Object satırı oluşturmalı ve her iki satırda da Id'yi kullanmalısınız.

DÜZENLEME: Davranışı da modellemek istiyorsanız, tablolar arasındaki miras ilişkilerini listeleyen ve her tablonun davranışını uygulayan derleme ve sınıf adını belirten bir Tür tablosuna ihtiyacınız olacaktır.

aşırılık gibi görünüyor, ama bu onu ne için kullanmak istediğinize bağlı!


Bu tartışma, kalıtımın modellenmesi ile değil, her tabloya birkaç sütun eklemekle sonuçlandı. Sorunun ve tartışmanın doğasını daha iyi yansıtmak için bu tartışmanın başlığının değiştirilmesi gerektiğini düşünüyorum.
bile Mien

1

SQL Simyayı (Python ORM) kullanarak, iki tür kalıtım yapabilirsiniz.

Tecrübe ettiğim tek şey bir tekli masa kullanmak ve ayırt edici bir sütuna sahip olmak. Örnekler için, bir Koyun veritabanı (şaka değil!) Tüm Koyunları tek bir tabloda depoladı ve Koçlar ve Koyunlar bu tablodaki bir cinsiyet sütunu kullanılarak işlendi.

Böylece tüm Koyunları sorgulayabilir ve tüm Koyunları alabilirsiniz. Veya yalnızca Ram ile sorgulayabilirsiniz ve yalnızca Rams'ı alır. Sadece Koç olabilen bir ilişkiye sahip olmak gibi şeyler de yapabilirsiniz (yani, Koyun Efendisi) vb.


1

Bazı veritabanı motorlarının zaten Postgres gibi yerel olarak miras mekanizmaları sağladığını unutmayın . Bak belgelere .

Örnek olarak, yukarıdaki yanıtta açıklanan Kişi / Çalışan sistemini şu şekilde sorgulayabilirsiniz:

  / * Bu, tüm kişilerin veya çalışanların ilk adını gösterir * /
  Kişiden ad SEÇİN; 

  / * Bu, yalnızca tüm çalışanların başlangıç ​​tarihini gösterir * /
  Çalışandan başlangıç ​​tarihini seçin;

Veritabanınızın seçimi budur, özellikle akıllı olmanıza gerek yoktur!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.