Neden DB'de bir enum depolarsınız?


69

Ben gibi bir dizi soru gördüğüm bu DB Çeteleler saklamak için tavsiyeler soran. Ama bunu neden yaptın merak ediyorum . Diyelim ki Personbir genderalan ve bir Genderenum ile bir varlık var . Daha sonra, kişi masamın sütun cinsiyeti var.

Doğruluğu uygulamanın bariz nedeninin yanı sıra, uygulamamda genderzaten sahip olduğum şeyleri haritalandırmak için neden fazladan bir tablo oluşturduğumu da anlamıyorum . Ve ben de bu kopyalamayı sevmiyorum.



1
Düzenli olarak değişebilecek verileri başka nerede saklarsınız? Tüm seçenekleri düşündüğünüzde, ne olursa olsun birisi gelir ve yeni bir seçenek eklemek isterse. O kodlanmış listeyi ayarlamaya hazır mısın? Birileri cinsiyetini kadın veya erkek dışında bir şey olarak vermek isteyebilir, örneğin interseks.
JB King,

4
@JBKing ... sadece Facebook'un cinsiyet listesine bakın.


3
Müşterileriniz "kandırılmış Tumblrites" ise, en azından işinde kalmayı planlıyorsanız, gereksinimlerine uygun bir şey yaratmanıza izin veren bir veritabanı şeması oluşturabilirsiniz.
Robotu

Yanıtlar:


74

Şimdi daha az anlayışlı, beklenti dolu bir örnek daha alalım. Burada bir numara var ve bir hata için öncelikler kümesi.

Veritabanında hangi değeri saklıyorsunuz?

Yani, depolamak olabilir 'C', 'H', 'M've 'L'veritabanındaki. Veya 'HIGH'benzeri. Bu stringly yazılan veri sorunu var . Orada geçerli bir değerler bilinen kümesi vardır ve eğer değildir veritabanında bu kümeyi depolamak, onunla çalışmak zor olabilir.

Neden verileri kodda saklıyorsunuz?

Kodda List<String> priorities = {'CRITICAL', 'HIGH', 'MEDIUM', 'LOW'};bu etkiyi veya başka bir şey var . Bu, bu verilerin çeşitli biçimlerini uygun formata sahip olduğunuz anlamına gelir (tüm büyük harfleri veritabanına ekliyorsunuz, ancak bunu görüntülüyorsunuz Critical). Kodunuz artık yerelleştirmek için de zor. Fikrin veritabanı temsilini kodda depolanan bir dizeye bağladınız.

Bu listeye erişmek için ihtiyacınız olan her yerde, kod çoğaltmaya ya da bir sürü sabit olan bir sınıfa ihtiyacınız olacaktır. Hiçbiri iyi seçenek değil. Ayrıca , bu verileri kullanabilecek başka uygulamalar da olduğunu unutmamak gerekir (diğer dillerde yazılmış olabilir - Java web uygulamasında kullanılan Crystal Reports raporlama sistemi ve içine Perl toplu iş besleme verileri vardır). Raporlama motorunun geçerli veri listesini bilmesi gerekir ( 'LOW'öncelikli olarak işaretlenmiş hiçbir şey olmazsa ne olur ve bunun rapor için geçerli bir öncelik olduğunu bilmeniz gerekir?) Ve toplu işin ne geçerli olduğu hakkında bilgi sahibi olur. değerler

Varsayalım, sen belki "Biz tek dil dükkanıysanız - her şey Java ile yazılmış" demek ve bu bilgileri içeren tek .jar var - ama şimdi sizin demektir uygulamalar sıkıca birbirlerine bağlanır ve bu .jar içeren veri. Her değişiklik olduğunda raporlama bölümünü ve toplu güncelleme bölümünü web uygulamasıyla birlikte serbest bırakmanız gerekir - ve bu sürümün tüm parçalar için sorunsuz bir şekilde uygulanacağını umarsınız .

Patronunuz başka bir öncelik istediğinde ne olur?

Patronun bugün geldi. Yeni bir öncelik var - CEO. Şimdi tüm kodu gidip değiştirmek ve yeniden derlemek ve yeniden konuşlandırmak zorundasınız .

'Tabloda enum' yaklaşımıyla, enum listesini yeni bir önceliğe sahip olacak şekilde güncellersiniz. Listeyi alan tüm kod veritabanından onu çeker.

Veriler nadiren tek başına durur

Önceliklerle, veriler iş akışları hakkında bilgi içerebilecek veya bu önceliği ya da neyi ayarlayabilecek başka tablolara girer .

Bir miktar soruda belirtildiği gibi cinsiyete geri dönme: Cinsiyet, kullanılan zamirlerle bir bağlantıya sahiptir: he/his/himve she/hers/her... ve bunu kodun kendisine zorla kodlamaktan kaçınmak istersiniz. Ve sonra patronunuz geliyor ve eklemenizin 'OTHER'cinsiyete sahip olduğunu eklemeniz gerekiyor (basit tutmak için) ve bu cinsle ilişki kurmanız gerekiyor they/their/them... ve patronunuz Facebook'un sahip olduğunu ve ... evet, evet.

Kendinizi bir enum tablosu yerine dizge biçiminde yazılan bir veri bitiyle sınırlandırarak, şimdi verilerle diğer bitler arasındaki ilişkiyi korumak için bu dizgiyi başka bir grup tabloda çoğaltmanız gerekir.

Peki ya diğer veri depoları?

Bunu nerede sakladığınız önemli değil, aynı ilke var.

  • priorities.propÖncelikler listesine sahip bir dosyanız olabilir . Bu listeyi bir özellik dosyasından okudunuz.
  • Bir girişi olan bir belge deposu veritabanına ( CouchDB gibi ) sahip olabilirsiniz enums(ve ardından JavaScript'te bir doğrulama işlevi yazabilirsiniz ):

    {
       "_id": "c18b0756c3c08d8fceb5bcddd60006f4",
       "_rev": "1-c89f76e36b740e9b899a4bffab44e1c2",
       "priorities": [ "critical", "high", "medium", "low" ],
       "severities": [ "blocker", "bad", "annoying", "cosmetic" ]
    }
    
  • Biraz şema içeren bir XML dosyanız olabilir:

    <xs:element name="priority" type="priorityType"/>
    
    <xs:simpleType name="priorityType">
      <xs:restriction base="xs:string">
        <xs:enumeration value="critical"/>
        <xs:enumeration value="high"/>
        <xs:enumeration value="medium"/>
        <xs:enumeration value="low"/>
      </xs:restriction>
    </xs:simpleType>
    

Temel fikir aynı. Veri deposunun kendisi, geçerli değerler listesinin saklanması ve uygulanması gereken yerdir. Buraya yerleştirerek, kod ve veriler hakkında düşünmek daha kolaydır. Her seferinde neye sahipseniz savunma olarak endişelenmenize gerek yok (büyük harf mi, küçük mü? Neden chriticalbu sütunda bir tür var ? Vb ...) çünkü veri deposundan ne aldığınızı biliyorsunuz Veri deposunun tam olarak ne göndermeni beklediğini - ve geçerli bir liste için veri deposunu sorgulayabilirsiniz.

Götürmek

Geçerli değerler kümesi kod değil veridir . Sen do için çaba gerekir KURU kod - ama çoğaltılması sorunu da kopyasını olmasıdır verileri ziyade veri olarak yerini saygı ve bir veritabanında depolayarak daha kodda.

Daha kolay veri deposuna karşı birden fazla uygulama yazma yapar ve sıkıca verinin kendisine bağlandığı her şeyi dağıtmak gerekir örneklerini zorunluluğunu ortadan kaldırır - nedeniyle henüz verilere kodunuzu çiftleşmiş.

CEOÖncelik eklendiğinde uygulamanın tamamını tekrar test etmeniz gerekmediğinden test uygulamalarını kolaylaştırır - çünkü önceliğin gerçek değerini önemseyen bir kodunuz yoktur.

Kod ve verileri birbirinden bağımsız olarak aktarabilmek, bakım yaparken hataları bulmayı ve düzeltmeyi kolaylaştırır.


6
Herhangi bir mantığı değiştirmek zorunda kalmadan kodunuza bir enum değeri ekleyebilirseniz (ve bunun yerelleştirilmiş bir gösterimi olsun), ilk etapta ek enum değerinin gerekliliğinden şüpheliyim. Üstelik bir sorunu analiz etmek için basit SQL sorguları ile veritabanı yedeklemelerini kolayca sorgulama yeteneğine değer verebilecek yaştayken, ORM'lerde bugünlerde temel veritabanına bakmak zorunda kalmadan çok iyi şeyler yapabilirsiniz. Burada yerelleştirme (zamirler) hakkında ne anlama geldiğini anlamıyorum - bu şeyler kesinlikle bir veritabanında olmamalı, ancak bir tür kaynak dosya derim.
Voo

1
@Vo zamirleri bu enumesque değeriyle ilgili diğer verilere bir örnektir . Veriler bir tabloya dahil edilmeden, katı bir şekilde yazılmış değerlerin uygun FK kısıtlamaları olmadan olması gerekir. Bir kaynak dosyasında zamirler (bunun gibi) varsa, veritabanıyla dosya arasında eşleştirmeniz gerekir (veritabanını güncelleyin ve dosyayı yeniden konuşlandırın). Bir yeniden dağıtım yapmak zorunda kalmadan anında yönetici arayüzü ile değiştirilebilir redmine numaralarını düşünün .

1
... aynı zamanda veritabanlarının çok dilli bir veri deposu olduğunu da unutmayın. ORM'nin bir parçası olarak onaylamanın bir dilde yapılması isteniyorsa, bu onaylamayı kullandığınız herhangi başka bir dilde çoğaltmanızı gerekli kılmıştır (yakın zamanda Python'un veritabanına veri aktarması için bir Java ön ucu ile çalıştım. - Java ORM ve Python sistemleri bir şeyler üzerinde hemfikir olmak zorunda - ve bu anlaşma (geçerli türler) veritabanının bir 'enum' tablosu ile zorlamasıyla en kolay şekilde uygulandı.).

2
@Voo enum kullanımının bugzilla ile aynı olduğunu "en önemli tablo sistemin tüm hatalarını içerir. Ciddiyet ve öncelik gibi tüm enum değerleri de dahil olmak üzere çeşitli hata özelliklerinden oluşur." - Serbest biçimli bir metin alanı değil, bilinen ve numaralandırılabilir kümelerden biri olan bir değer. Bu bir değil derleme zamanı enum ancak onun hala enumish. Ayrıca bakınız Mantis .

1
Yani onaylamak için - amacınız insanların asla Enums kullanmaması gerektiğidir? Açık değildi.
niico,

18

Bunlardan hangisinin sorguyu okurken hata üretmesi daha olasıdır?

select * 
from Person 
where Gender = 1

Veya

select * 
from Person join Gender on Person.Gender = Gender.GenderId
where Gender.Label = "Female" 

İnsanlar SQL'de enum tabloları yaparlar çünkü ikincisini daha okunaklı bulurlar - bu da SQL yazma ve sürdürmede daha az hataya yol açar.

Cinsiyet doğrudan bir dize haline gelebilir Person, ancak o zaman davayı denemek ve uygulamak zorunda kalacaksınız. Ayrıca, DB'nizin işleri optimize etmesinde ne kadar harika olduğuna bağlı olarak, dizeler ve tam sayılar arasındaki fark nedeniyle tablo için depolama isabetini ve sorgu süresini artırabilirsiniz.


5
Ama sonra masalara katılıyoruz. Varlığımın iki kodu varsa, sadece basit bir sorgu için üç tabloya katılacağım.
user3748908

11
@ user3748908 - yani? Katılımlar, DB'lerin iyi olduğu ve alternatiflerin daha kötü olduğu - en azından bu rotayı seçenlerin gözünde.
Telastyn,

8
@ user3748908: Sadece veri tabanları birleştirme işleminde gerçekten başarılı değiller, aynı zamanda tutarlılığı sağlamada da gerçekten başarılılar. Tutarlılığı arttırmak gerçekten işe yarıyor, bir sütunu diğerinin tanımlayıcı satırındaki bir tabloda işaretleyebiliyorsanız ve "bu sütunun değerinin o tablodaki tanımlayıcılardan biri olması gerektiğini" söylerken gerçekten işe yarar.
Blrfl

2
Bunların hepsi doğru, ancak performans nedenlerinden dolayı birleşmeleri feda etmeniz gereken birçok durum var. Beni yanlış anlamayın, tamamen bu tür bir tasarım ve birleşme hakkındayım, ancak performans nedeniyle bir araya gelmeye ihtiyaç duymazsanız dünyanın bitmeyeceğini atıyorum.
JonH

3
@JonH performans nedenlerinden dolayı referans tablolarına katılmaktan vazgeçmek zorundaysanız, daha büyük bir sunucu satın almanız ya da çok sayıda alt sorgu yoluyla tahminleri zorlamaya çalışmaktan vazgeçmeniz gerekir (ne yaptığınızı bildiğinizi varsayıyorum). Referanslar tabloları, DB'yi başlattıktan birkaç saniye sonra önbelleğinizde bulunması gereken öğelerdir.
Ben,

10

İnsanların henüz bundan bahsetmediğine inanamıyorum.

Yabancı anahtarlar

Enum'u veritabanınızda tutarak ve bir enum değeri içeren tabloya bir yabancı anahtar ekleyerek , hiçbir kodun o sütun için yanlış değerler girmemesini sağlayabilirsiniz . Bu, veri bütünlüğünüze yardımcı olur ve IMO'nun enums için tablolara sahip olması gereken en açık nedendir.


Soru sadece 5 satır uzunluğunda ve açıkça "Doğruluğu uygulamanın açık nedeninin yanı sıra" demiştir. Bu yüzden kimse bundan bahsetmedi çünkü OP bunun açık olduğunu ve başka gerekçeleri aradığını söylüyor - PS: Size katılıyorum, bu yeterince iyi bir neden.
user1007074

6

Seninle aynı fikirde olan kamptayım. Cinsiyet kodunu kodunuzda tutarsanız ve veritabanınızdaki bir tblGender kodunu kullanırsanız, bakım sırasında sorun yaşayabilirsiniz. Bu iki varlığın aynı değerlere sahip olması gerektiğini ve böylece birinde yapmanız gereken değişiklikleri diğerinde yapmanız gerektiğini belgelemeniz gerekecektir.

Daha sonra enum değerlerini aşağıdaki gibi saklı yordamlarınıza geçirmeniz gerekir:

create stored procedure InsertPerson @name varchar, @gender int
    insert into tblPeople (name, gender)
    values (@name, @gender)

Ancak, bu değerleri bir veritabanı tablosunda tutarsanız, bunu nasıl yapacağınızı düşünün:

create stored procedure InsertPerson @name varchar, @genderName varchar
    insert into tblPeople (name, gender)
    select @name, fkGender
    from tblGender
    where genderName = @genderName --I hope these are the same

Tabii ilişkisel veri tabanları bir araya getirilerek oluşturuldu, ancak hangi sorgunun okunması daha kolay?


İşte başka bir örnek sorgu:

create stored procedure SpGetGenderCounts
    select count(*) as count, gender
    from tblPeople
    group by gender

Bunu bununla karşılaştırın:

create stored procedure SpGetGenderCounts
    select count(*) as count, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender
    group by genderName --assuming no two genders have the same name

İşte başka bir örnek sorgu:

create stored procedure GetAllPeople
    select name, gender
    from tblPeople

Bu örnekte, sonuçlarınızdaki cinsiyet hücresini int'den bir enuma dönüştürmeniz gerekeceğini unutmayın. Ancak bu dönüşümler kolaydır. Bunu bununla karşılaştırın:

create stored procedure GetAllPeople
    select name, genderName
    from tblPeople
    inner join tblGender on pkGender = fkGender

Enum tanımlarını veritabanından uzak tutma fikrinizle devam ederken, bu sorguların tümü daha küçük ve daha bakımlıdır.


1
Ya cinsiyet olsa bile. Sanırım alan olarak cinsiyete fazla takılıyoruz . OP, "Öyleyse, Öncelikli bir alana sahip olduğum bir varlığım var diyelim" demiş olsaydı - cevabınız değişir mi?

4
@MichaelT "Öncelikli" değerlerin listesi, en azından verinin bir parçası olduğu ölçüde kodun bir parçasıdır. Çeşitli öncelikler için grafik simgeler görüyor musunuz? Veritabanından çıkarılmalarını beklemiyor musun? Ve bunun gibi şeyler temalı ve stilli olabilir ve yine de DB'de depolanan aynı değer aralığını temsil eder. Zaten sadece veritabanında değiştiremezsiniz; Senkronize etmek için sunum kodunuz var.
Eugene Ryabtsev

1

Veri analizinde kullanılabileceği için bir Cinsiyet tablosu oluşturacağım. Bir rapor oluşturmak için veritabanındaki tüm Erkek veya Kadın Kişileri arayabilirim. Verilerinizi ne kadar çok izleyebiliyorsanız, trend bilgilerinizi o kadar kolay keşfedersiniz. Açıkçası, bu çok basit bir numaralandırmadır, ancak karmaşık numaralandırmalar için (dünyadaki ülkeler veya ülkeler gibi), özel raporlar oluşturmayı kolaylaştırır.


1

Öncelikle, veritabanının yalnızca bir uygulama tarafından kullanılıp kullanılmayacağına ya da birden fazla uygulamanın onu kullanma potansiyeli olup olmadığına karar vermeniz gerekir. Bazı durumlarda veritabanı, bir uygulama için bir dosya biçiminden başka bir şey değildir (SQLite veritabanları bu konuda sıklıkla kullanılabilir). Bu durumda, enum tanımını tablo olarak çoğaltma biti genellikle iyi olabilir ve daha anlamlı olabilir.

Ancak, veritabanına erişen birden fazla uygulamanın bulunma olasılığını göz önüne almak istediğinizde, enum için bir tablo çok mantıklıdır (diğer cevaplar neden daha ayrıntılı olarak açıklanmaktadır). Dikkate alınacak diğer şey siz veya başka bir geliştirici ham veritabanı verilerine bakmak isteyeceksiniz. Eğer öyleyse, bu başka bir uygulama kullanımı olarak kabul edilebilir (yalnızca laboratuvar göstergesinin ham SQL olduğu bir alan).

Kodda tanımlanmış numaralandırmayı (temizleyici kodu ve derleme zamanı denetimi için) ve veritabanındaki bir tablo varsa, ikisinin senkronize olduğunu doğrulamak için birim testleri eklemenizi öneririm.


1

İş mantığını koda sürmek için kullanılan bir kod numaralandırmanız varsa, yukarıda / aşağıda açıklanan birçok nedenden dolayı DB'deki verileri temsil etmek için bir tablo oluşturmanız gerekir. DB değerlerinin kod değerleriyle eşit kalmasını sağlamak için birkaç ipucu:

  1. Tablodaki kimlik alanını Kimlik sütunu yapmayın. Kimlik ve Açıklama'yı alan olarak ekleyin.

  2. Tabloda, geliştiricilerin değerlerin yarı statik / kod numaralandırmasına bağlı olduğunu bilmelerine yardımcı olan farklı bir şey yapın. Diğer tüm arama tablolarında (genellikle değerlerin kullanıcılar tarafından eklenebileceği yerlerde) tipik olarak bir LastChangedDateTime ve LastChangedBy'ye sahibim, ancak bunları enum ile ilgili tablolarda bulundurmamak, yalnızca geliştiriciler tarafından değiştirilebilir olduklarını hatırlamama yardımcı oluyor. Bunu belgeleyin.

  3. Numaralandırmadaki her değerin karşılık gelen tabloda olduğunu ve yalnızca bu değerlerin karşılık gelen tabloda olduğunu görmek için doğrulama kodu oluşturun. Yapma sonrası çalıştırılan otomatikleştirilmiş "sağlık testleri" uygulamanız varsa, orada. Değilse, uygulama IDE'de çalışırken kodun uygulama başlangıcında otomatik olarak çalışmasını sağlayın.

  4. Üretim oluşturma, aynı şeyi yapan, ancak DB içinden SQL komut dosyaları sunar. Doğru yaratılırlarsa, çevre göçlerine de yardımcı olurlar.


0

Ayrıca verilere kimin eriştiğine de bağlıdır. Sadece iyi bir uygulama varsa. Bir veri ambarı veya raporlama sistemi eklerseniz. Bu kodun ne anlama geldiğini, kodun insan tarafından okunabilir versiyonunun ne olduğunu bilmeleri gerekir.

Genellikle, tür tablosu koddaki bir enum olarak çoğaltılmaz. Tür tablosunu önbelleğe alınmış bir listeye yükleyebilirsiniz.

Class GenderList

   Public Shared Property UnfilteredList
   Public Shared Property Male = GetItem("M")
   Public Shared Property Female = GetItem("F")

End Class

Genellikle, yazın gelir ve gider. Yeni türün eklendiği tarih için tarih gerekir. Belirli bir türün ne zaman kaldırıldığını bilin. Sadece gerektiğinde görüntüleyin. Bir müşteri cinsiyet olarak "transeksüel" ister ancak diğer müşteriler istemezse ne olur? Bu bilgilerin tümü en iyi şekilde veritabanında depolanır.

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.