Birden çok yabancı anahtarın virgülle ayrılması yanlış mı ve öyleyse neden?


31

İki tablo var: Dealve DealCategories. Bir anlaşma birçok anlaşma kategorisine sahip olabilir.

Bu yüzden uygun yol DealCategories, aşağıdaki yapı ile adlandırılmış bir tablo oluşturmak olmalıdır :

DealCategoryId (PK)
DealId (FK)
DealCategoryId (FK)

Ancak, dış kaynak ekibimiz Dealtablodaki birden fazla kategoriyi şu şekilde depoladı :

DealId (PK)
DealCategory -- In here they store multiple deal ids separated by commas like this: 18,25,32.

Yaptıklarının yanlış olduğunu hissediyorum, ancak bunun neden doğru olmadığını açık bir şekilde nasıl açıklayacağımı bilmiyorum.

Bunun yanlış olduğunu onlara nasıl açıklamalıyım? Ya da belki ben yanlış bir ve bu kabul edilebilir?



7
daha fazla zarar vermeden hemen önce dış kaynaklı ekibi ateşle ... (-_-)
Rafa

Yanıtlar:


49

Evet bu korkunç bir fikir.

Gitmek yerine:

SELECT Deal.Name, DealCategory.Name
FROM Deal
  INNER JOIN
     DealCategories ON Deal.DealID = DealCategories.DealID
  INNER JOIN
     DealCategory ON DealCategories.DealCategoryID = DealCategory.DealCategoryID
WHERE Deal.DealID = 1234

Şimdi gitmelisin:

SELECT Deal.ID, Deal.Name, DealCategories
FROM Deal
WHERE Deal.DealID = 1234

Ardından, virgül listesini bireysel numaralara bölmek için uygulama kodunuzda bir şeyler yapmanız ve ardından veritabanını ayrı olarak sorgulamanız gerekir:

SELECT DealCategory.Name
FROM DealCategory
WHERE DealCategory.DealCategoryID IN (<<that list from before>>)

Bu tasarım antipattern, ilişkisel modellemenin tam olarak yanlış anlaşılmasından kaynaklanmaktadır (Tablolardan korkmak zorunda değilsiniz. Tablolar arkadaşlarınızdır. Bunları kullanın) veya tuhaf bir şekilde yanlış yönlendirilmiş bir inancı virgülle ayrılmış bir listeye alıp bölmek daha hızlı Uygulama kodunda bir link tablosu eklemek olduğundan ( asla değil ). Üçüncü seçenek, yabancı anahtarlar ayarlayabilmeleri için SQL ile yeteri kadar güvenilmez / yetkin olmalarıdır, ancak bu durumda, ilişkisel bir modelin tasarımı ile ilgisi olmamalıdır.

SQL Antipatterns ( Karwin , 2010), bu antipattern'e ('Jaywalking' adını verdiği) 15-23. Sayfaların tamamını vermektedir. Ayrıca, yazar SO'da benzer bir soru yayınlamıştır . Not ettiği anahtar noktalar (bu örneğe uygulandığı gibi):

  • Belirli bir kategorideki tüm anlaşmaları sorgulamak oldukça karmaşıktır (bu sorunu çözmenin en kolay yolu düzenli bir ifadedir, ancak düzenli bir ifade kendi başına bir sorundur).
  • Yabancı anahtar ilişkileri olmadan referans bütünlüğünü uygulayamazsınız. DealCategory silmek nr. # 26, sonra, uygulama kodunuzda, # 26 kategorisine referansları aramak için her bir anlaşmayı gözden geçirmeli ve silmelisiniz. Bu, veri katmanında ele alınması gereken bir şeydir ve uygulamanızda ele almak zorunda kalmak çok kötü bir şeydir .
  • Agrega sorgular ( COUNT, SUMvb), yine, 'neredeyse imkansız' to 'karmaşık' değişir. Geliştiricilerinize, bu kategorideki fırsatların sayısının yer aldığı, size tüm kategorilerin listesini nasıl alabileceklerini sorun. Düzgün bir tasarıma sahip, dört satır SQL.
  • Güncellemeler çok daha zorlaşıyor (yani beş kategoride yapılan bir anlaşmanız var, ancak ikisini kaldırmak ve üç tane daha eklemek istiyorsunuz). Bu, uygun bir tasarıma sahip üç SQL hattıdır.
  • Sonunda VARCHARliste uzunluğu sınırlamaları ile karşılaşacaksınız. 4000 karakterden fazla virgülle ayrılmış bir listeniz olsa da, canavarın cehennem kadar yavaş olacağına dair bir ihtimal var.
  • Bir listeyi veritabanından çıkarmak, bölmek ve ardından başka bir sorgu için veritabanına geri dönmek, aslında bir sorgudan daha yavaştır.

TLDR: Temelde kusurlu bir tasarım, iyi ölçeklenmeyecek, en basit sorgulara bile ek bir karmaşıklık getirecek ve kullanıma hazır hale getirmesini yavaşlatacak.


1
Simon, biri aynı soruyu yaptı ( dba.stackexchange.com/questions/17824/… ), ancak aynı FK ve PK'nın neden aynı masada olduğunu 3FN'yi frenleyen net bir fikrim yok.
jcho360

2
Fırsatlar ve Kategoriler arasında veya bir çeşit Kategoriler heirarşi arasında bir çok ilişki kurmak istediklerinden tam olarak emin değildim. Her iki durumda da, ana noktaya bir kenar çizgisiydi, bağlantı tablosu yerine virgülle ayrılmış alanlar olmak kötü bir fikirdir.
Simon Righarts,

4

Ancak, dış kaynak ekibimiz birden fazla kategoriyi Deal tablosunda şu şekilde depoladı:

DealId (PK) DealCategory - Burada, bu şekilde virgüllerle ayrılmış çoklu anlaşma kimliklerini depolar: 18,25,32.

Bu, yalnızca belirli bir anlaşma için kategorileri sorgulamanız gerektiğinde iyi bir tasarım .

Ancak, belirli bir kategorideki tüm fırsatları bilmek istiyorsanız korkunç.

Ayrıca güncellemeler, sayımlar, katılımlar, vs. gibi başka bir şey yapmayı gerçekten zor ve hataya açık hale getirir.

Denormalizasyonun yeri vardır, ancak aynı verilere karşı yapabileceğiniz diğer tüm kişilerin pahasına bir tür sorgu için optimize edildiğini aklınızda bulundurmanız gerekir. Her zaman bir düzende sorgulayacağınızı biliyorsanız, denormalize edilmiş tasarımı kullanmanız size bir avantaj sağlayabilir. Ancak, sorgu türleri için daha fazla esnekliğe ihtiyaç duyma ihtimaliniz varsa, normal bir tasarımla devam edin.

Diğer optimizasyon türleri gibi, denormalizasyonun haklı olup olmadığına karar vermeden önce hangi soruları çalıştıracağınızı bilmeniz gerekir.


1
Gerçekten virgülle ayrılmış çocuk kimlikleri olan bir dize yararlı olduğunu düşünüyor musunuz? Demek istediğim, uygulama önce okumak, kimlikleri ayrıştırmak ve tüm çocukları sorgulamak zorunda kaldı select * from DealCategories where DealId in (1,2,3,4,...). Veri tabanı tasarımı konusunda benden daha fazla deneyime sahipsin, bu yüzden bazı durumlarda çok özel durumlarda bu tür "aşırı ayarlama" için iyi bir nedenin var . Bunu haklı çıkarmak için tek fikrim select, Deal / DealCategory'te çok yüksek bir yük. Bu bana, herhangi bir DB tasarım bilgisine sahip olmayan bir dış kaynak ekibine benziyor, tablolar yaratmanın ötesinde yarattı.
Erik Hart

1
@ErikHart, bu denormalizasyon ve faydalı olabilir , ama benim açımdan görmeniz gereken, tamamen çalıştırmanız gereken sorulara bağlı olmasıdır. Denormalizasyonun, optimize ettiği sorgu dışında tüm sorguların daha kötü performans göstermesine neden olduğunu haklısınız. Yalnızca bir sorguyu çalıştırmanız gerekiyorsa ve diğer sorguları umursamıyorsanız, bu bir kazançtır. Ancak bunlar nadir görülen durumlardır, çünkü genellikle verileri çeşitli şekillerde sorgulama esnekliği istiyoruz.
Bill Karwin

1
@ErikHart, bu dış kaynak ekibine bu verilere karşı yalnızca bir sorgu içeren proje spesifikasyonları verilmiş olsaydı, yalnızca bu özel sorgu için bir optimizasyon tasarlayabilirlerdi. Başka bir deyişle, "Siz istediniz, anladınız." Ancak dış kaynak sağlayıcısının verinin gelecekteki kullanımlarını planlamak için bir nedeni yoktur - başvuruyu teknik özellikte yazılanların harfine uygularlar.
Bill Karwin

1

Bir sütundaki birden çok değer 1. normal forma göredir.

Ayrıca tablolarda veri tabanına bağlanılacağından kesinlikle hız kazancı yoktur. Önce bir dize okuyup ayrıştırmanız ardından "Deal" için tüm kategorileri seçmeniz gerekir.

Doğru uygulama, DealId ve DealCategoryId ile "DealDealCategoriler" gibi bir bağlantı tablosu olacaktır.

Kötü hiyerarşi uygulaması?

Ayrıca, başka bir DealCategoriye DealCategorilerdeki bir FK, DealCategoriler hiyerarşisinin / ağacının kötü bir şekilde uygulanmasına benziyor. Bir Ana Kimlik (bitişiklik listesi olarak adlandırılır) ilişkisi üzerinden ağaçlarla çalışmak bir acıdır!

Hiyerarşileri uygularken Yuvalanmış Kümeleri (okunması iyi, ancak değiştirilmesi zor) ve Kapanış Tablolarını (en iyi genel performans, ancak muhtemelen yüksek bellek kullanımı - muhtemelen DealKategorileriniz için çok fazla değildir) kontrol edin!

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.