Veritabanı tasarımı: “(çoktan çoğa) çoktan çoğa” ilişkisini normalleştirme


14

Kısa versiyon

Varolan bir çok-çok katılmak her çift için sabit sayıda ek özellikler eklemek zorunda. Aşağıdaki şemalara atlamak, avantajları ve dezavantajları açısından Temel Davayı genişleterek bunu başarmanın en iyi yolu Seçenek 1-4'ten hangisidir? Yoksa burada düşünmediğim daha iyi bir alternatif var mı?

Daha uzun versiyon

Şu anda bir ara birleştirme tablosu aracılığıyla, çok-çok ilişkisi iki tablo var. Şimdi varolan nesnelerin çift ait özelliklere ek bağlantılar eklemeniz gerekir. Özellik tablosundaki bir giriş birden çok çift (hatta bir çift için birden çok kez kullanılabilir) olsa da, her çift için bu özelliklerin sabit bir sayısı var. Bunu yapmanın en iyi yolunu belirlemeye çalışıyorum ve durumu nasıl düşüneceğimizi sıralamakta zorlanıyorum. Anlamsal olarak, aşağıdakileri eşit derecede iyi olarak tanımlayabilirim gibi görünüyor:

  1. Sabit sayıda ek özellik kümesine bağlı bir çift
  2. Birçok ek özelliğe bağlı bir çift
  3. Bir özellik kümesine bağlı birçok (iki) nesne
  4. Birçok özelliğe bağlı birçok nesne

Misal

İki nesne türü, X ve Y, her biri benzersiz kimlikleri ve objx_objysütunları olan bir bağlantı tablosu x_idve y_idbirlikte bağlantı için birincil anahtarı oluşturur. Her X birçok Y ile ilişkili olabilir veya bunun tersi de geçerlidir. Bu benim mevcut çoktan çoğa ilişkimin kurumu.

Temel Durum

Temel durum

Şimdi ek olarak başka bir tabloda tanımlanmış bir dizi özelliğe ve belirli bir (X, Y) çiftinin P özelliğine sahip olması gereken bir dizi koşula sahibim. Koşulların sayısı sabittir ve tüm çiftler için aynıdır. Temel olarak "C1 durumunda, çift (X1, Y1) P1 özelliğine sahiptir", "C2 durumunda çift (X1, Y1) P2 özelliğine sahiptir" vb. Derler. tablo.

seçenek 1

Benim Mevcut durumda orada tam olarak üç tür durumlar vardır ve bir olasılık sütunları eklemek için, yani daha o artmasını bekliyoruz için hiçbir neden yok c1_p_id, c2_p_idve c3_p_idhiç featx_featybir verilen için belirtilerek, x_idve y_idhangi özellik, p_idüç durumların her kullanılmak üzere .

seçenek 1

Bu benim için harika bir fikir gibi görünmüyor, çünkü bir özelliğe uygulanan tüm özellikleri seçmek için SQL'i karmaşıklaştırıyor ve daha fazla koşula kolayca ölçeklenmiyor. Bununla birlikte, (X, Y) çifti başına belirli sayıda koşulun gerekliliğini zorlar. Aslında, bunu yapan tek seçenek budur.

seçenek 2

Bir koşul tablosu oluşturun condve birleştirme tablosunun birincil anahtarına koşul kimliğini ekleyin.

seçenek 2

Bunun bir dezavantajı, her bir çift için koşul sayısını belirtmemesidir. Bir diğeri, sadece ilk ilişkiyi düşündüğümde,

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Sonra DISTINCTyinelenen girişleri önlemek için bir madde eklemek zorunda. Bu, her bir çiftin sadece bir kez var olması gerçeğini kaybetmiş görünüyor.

Seçenek 3

Birleştirme tablosunda yeni bir 'çift tanıtıcısı' oluşturun ve birincisi ile özellikler ve koşullar arasında ikinci bir bağlantı tablosu oluşturun.

Seçenek 3

Bu, her çift için sabit sayıda koşulun uygulanmaması dışında en az dezavantaja sahip gibi görünüyor. Mevcut kimliklerden başka bir şey tanımlamayacak yeni bir kimlik oluşturmak mantıklı mı?

Seçenek 4 (3b)

Temel olarak Seçenek 3 ile aynı, ancak ek kimlik alanı oluşturulmadan. Bu, her iki orijinal kimliğin yeni birleştirme tablosuna yerleştirilmesiyle gerçekleştirilir, böylece bunun yerine x_idve y_idalanları içerir xy_id.

Seçenek 4

Bu forma ek bir avantaj, mevcut tabloları değiştirmemesidir (henüz üretimde olmasalar da). Bununla birlikte, temel olarak tüm bir tabloyu birkaç kez çoğaltır (veya yine de bu şekilde hisseder), bu yüzden ideal görünmez.

özet

Benim düşüncem, Seçenek 3 ve 4'ün her ikisiyle de gidebileceğim kadar benzer olduğudur. Muhtemelen şimdiye kadar değilse Seçenek 1, aksi takdirde daha makul görünüyor yapar özellikleri bağlantıları küçük bir sabit gereksinimi için olurdu. Bazı çok sınırlı testlere dayanarak, DISTINCTsorgularıma bir cümle eklemek bu durumda performansı etkilemiyor gibi görünüyor, ancak yerleştirmenin neden olduğu doğal çoğaltma nedeniyle Seçenek 2'nin durumu ve diğerlerini de temsil ettiğinden emin değilim bağlantı tablosunun birden çok satırında aynı (X, Y) çiftleri.

Bu seçeneklerden biri benim en iyi yolum mu yoksa dikkate almam gereken başka bir yapı var mı?


Genel olarak 1 ve 4 en iyi seçenek gibi görünüyor, kabul ediyorum. Sabit (3) özellik sayısını seçenek 4 ile zorlamak kolay olmayacaktır, ancak bence bu mümkün.
ypercubeᵀᴹ

İçin DISTINCTmaddesi, ben bağlantılar 2. sonunda biri gibi bir sorgunun düşünüyordum xve yiçinden xycifade etmez ama cben eğer ... So (x_id, y_id, c_id)kısıtlı UNIQUEsatırlarla (1,1,1)ve (1,1,2)sonra SELECT x.id, y.id FROM x JOIN xyc JOIN y, geri özdeş iki alırsınız satırlar, (1,1)ve (1,1).
Michael Underwood

1
Ah tamam. Yine de 2. seçeneği işten çıkarırdım. 1 ya da 4 ile giderdim.
ypercubeᵀᴹ

Ne kadar çok düşünürsem, özellik sayısını tam olarak üçle sınırlamanın, gereksinimlerim için en az önemli olduğunu hissediyorum. Önümüzdeki kısa süre içinde ek yapıcı geribildirimleri engellemek, muhtemelen bu noktada # 4 ile gideceğim. Girişiniz için teşekkürler, @ ypercube!
Michael Underwood

Yanıtlar:


7
  • seçenek 1

    * Bu benim için harika bir fikir gibi görünmüyor, çünkü bir özelliğe uygulanan tüm özellikleri seçmek için SQL karmaşıklaştırır…

    Sorgu SQL'ini karmaşıklaştırmak zorunda değildir (aşağıdaki sonuca bakın).

    … Ve kolayca daha fazla koşulla ölçeklendirilmez…

    Sabit sayıda koşul olduğu ve düzinelerce veya yüzlerce olmadığı sürece, daha fazla koşula kolayca ölçeklenir.

    Bununla birlikte, (X, Y) çifti başına belirli sayıda koşulun gerekliliğini zorlar. Aslında, bunu yapan tek seçenek budur. *

    Öyle ve bir yorumda bunun "gereksinimlerimden en az önemli" olduğunu söylemenize rağmen, bunun hiç önemli olmadığını söylemediniz.

  • seçenek 2

    Bunun bir dezavantajı, her bir çift için koşul sayısını belirtmemesidir. Başka bir şey, ben sadece ilk ilişkiyi düşünürken ... Sonra yinelenen girişleri önlemek için bir DISTINCT yan tümcesi eklemek zorunda ...

    Bahsettiğiniz komplikasyonlar nedeniyle bu seçeneği reddedebilirsiniz. objx_objyTablo sorguları bazıları için itici tablo (örneğin, "seçeneğini bir özellik uygulanan tüm özellikleri" Ben tüm özellikleri anlamında alıyorum, bir uygulanan olması muhtemeldir objxveya objy). Önceden uygulamak için bir görünüm kullanabilirsiniz, DISTINCTbu yüzden sorguları karmaşık hale getirme meselesi değildir, ancak bu çok az kazanç için performans açısından çok kötü bir şekilde ölçeklendirilecektir.

  • Seçenek 3

    Mevcut kimliklerden başka bir şey tanımlamayacak yeni bir kimlik oluşturmak mantıklı mı?

    Hayır, öyle değil - Seçenek 4 her açıdan daha iyidir.

  • Seçenek 4

    … Temelde bir tablonun tamamını birden çok kez çoğaltır (ya da zaten böyle hisseder) bu yüzden ideal görünmez.

    Bu seçenek gayet iyi - özellik sayısı değişkense veya değişime tabi ise, ilişkileri kurmanın bariz yolu budur

Sonuç

Başına özellik sayısının objx_objysabit olması muhtemelse ve bir avuç ekstradan fazlasını ekleyeceğinizi hayal edemiyorsanız, tercihim seçenek 1 olacaktır . Ayrıca, 'özellik sayısı = 3' kısıtlamasını zorlayan tek seçenektir - seçenek 4'te benzer bir kısıtlamanın uygulanması c1_p_id, xy tablosuna zaten * sütunlarının eklenmesini içerebilir .

Bu durum hakkında çok fazla umursamıyorsanız ve özellik sayısının istikrarlı olacağından şüphe etmek için bir nedeniniz varsa, seçenek 4'ü seçin.

Hangisinden emin değilseniz, seçenek 1'i seçin - daha basittir ve başkalarının söylediği gibi, seçeneğiniz varsa kesinlikle daha iyidir. Eğer bir özelliğe uygulanan tüm özellikleri seçmek SQL'i karmaşıklaştırdığı için seçenek 1 "ertelenirse ..." 4. seçenekte yer alan ekstra tablo ile aynı verileri sağlamak için bir görünüm oluşturmanızı öneririm:

seçenek 1 tabloları:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

'öykün' seçeneği 4'e bakın:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"bir özelliğe uygulanan tüm özellikleri seç":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle burada


-3

Bu seçeneklerden herhangi birinin işe yarayabileceğine inanıyorum, ancak koşulların sayısı 3'te gerçekten sabitse seçenek 1 ve değilse seçenek 2 ile giderdim. Occam'ın tıraş makinesi de veritabanı tasarımı için çalışıyor, diğer tüm faktörler en basit tasarıma eşit olmak genellikle en iyisidir.

Sıkı veritabanı normalleştirme kurallarına uymak istiyorsanız, koşul sayısının sabit olup olmadığına bakılmaksızın 2 ile gitmeniz gerektiğine inanıyorum.

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.