Enum veri türünü kullanmak yerine yeni bir veritabanı tablosu oluşturmak gereksiz midir?


38

Diyelim ki sunduğum 4 tür hizmetim var (sık sık değişme olasılığı yoktur):

  • Test yapmak
  • tasarlamak
  • Programlama
  • Diğer

Her birinin yukarıdaki kategorilerden birine giren 60-80 gerçek hizmetim olduğunu varsayalım. Örneğin, 'hizmet', "A tekniği kullanarak Test Programı" olabilir ve "Test" türündedir.

Onları bir veritabanına kodlamak istiyorum. Birkaç seçenek buldum:

Seçenek 0:

VARCHARHizmet türünü doğrudan bir dize olarak kodlamak için doğrudan kullanın

Seçenek 1:

Veritabanını kullan enum. Ancak, enum kötüdür

Seçenek 2:

iki tablo kullanın:

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

Referans bütünlüğünün tadını bile çıkarabilirim:

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

Kulağa hoş geliyor, evet?

Ama yine de bir şeyleri kodlamam ve tamsayılarla uğraşmam gerekiyor, yani tabloyu doldururken. Veya tabloyu doldururken veya doldururken ayrıntılı programlama veya DB yapıları oluşturmalıyım. Yani, doğrudan veritabanıyla ilgilenirken veya programlama tarafında yeni nesne yönelimli varlıklar yaratırken ve onları doğru çalıştırdığımdan emin olduğumda JOIN'lar.

Seçenek 3:

Kullanmayın enum, iki tablo kullanmayın, sadece bir tamsayı sütunu kullanın

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

Bu, şeylerin kod tarafında daha fazla ek yük gerektiren bir 'sahte enum' gibi, yani bunu bilmek {2 == 'Programming'}ve onunla başa çıkmak gibi .

Soru:

Şu anda , kavramlar altında yönlendirilen Seçenek 2'yi kullanarak uyguladım.

  1. enum kullanmayın (seçenek 1)
  2. veritabanını elektronik tablo olarak kullanmaktan kaçının (seçenek 0)

Fakat bunun programlama ve bilişsel yükü açısından benim için zararlı göründüğünü hissetmeye yardım edemem - iki masanın farkında olmalıyım ve iki masayla başa çıkmalıyım.

'Daha az boşa giden bir yol' için bakıyorum Option 3. BT daha hafiftir ve temelde aynı kod yapıları için çalışır (küçük değişikliklerle ancak karmaşıklık ve yapı temelde aynıdır, ancak tek bir tablo ile)

Bence ideal olarak her zaman boşa gitmiyor ve her iki seçenek için de iyi durumlar var, fakat Seçenek 2'nin ne zaman ve Seçenek 3'ü ne zaman kullanması gerektiği konusunda iyi bir rehber var mı?

Sadece iki tip olduğunda (ikili)

Bu soruya biraz daha fazla eklemek için ... aynı mekanda, servis satır öğesine uygulanabilecek "Standart" veya "İstisna" Hizmeti'nin ikili seçeneğine sahibim. Seçenek 3 kullanarak bunu kodladım .

Sadece {"Standard", "Exception"} değerlerini tutmak için yeni bir tablo oluşturmamayı seçtim. Bu yüzden benim sütunum {0, 1} tutar ve sütun ismim çağrılır exceptionve kodum bir çeviri yapıyor {0, 1} => {STANDARD, EXCEPTION}(programlama dilinde sabit olarak kodladım)

Şimdiye kadar bu şekilde ya da değil ..... (seçenek 2 veya seçenek 3'ü beğenmedim). Seçenek 2'yi 3'ten daha üstün buluyorum, ancak daha fazla ek yüke sahipken, yine de 2 ve 3'ten hangisini kullanırsam kullansam da tamsayı olarak kodlama olaylarından kaçamıyorum.

ORM

Bazı bağlamları eklemek için, cevapları okuduktan sonra - bir ORM kullanmaya yeni başlamıştım (yakın zamanda), benim durumumda Doktrin 2. Ek Notlar aracılığıyla DB şemasını tanımladıktan sonra veritabanını doldurmak istedim. Veri setimin tamamı nispeten küçük olduğundan, nasıl çalıştığını görmek için programlama yapıları kullanmayı denemek istedim.

Önce service_types'yi, ardından service_line_items'yi, gerçek bir elektronik tablonun var olan bir listesi olduğu için doldurdum . Bu nedenle, 'standart / istisna' ve 'Test' gibi şeylerin hepsi e-tablodaki dizelerdir ve bunları DB'ye kaydetmeden önce uygun türlere kodlanmaları gerekir.

Bu SO cevabını buldum: doctrine2'de ENUM yerine ne kullanıyorsunuz? DB'nin enum yapısını kullanmamak, bir INTalan kullanmak ve programlama dilinin 'const' yapısını kullanarak türlerini kodlamak için önerilmektedir .

Ancak yukarıdaki SO sorusunda belirtildiği gibi, tamsayıları doğrudan kullanmaktan kaçınabilir ve dil yapılarını - sabitleri - tanımlandıktan sonra kullanabilirim ....

Ama yine de .... nasıl çevirdiğiniz önemli değil, stringbir tür olarak başlıyorsam, bir ORM kullanırken bile ilk önce uygun bir türe dönüştürmem gerekir.

Öyleyse $str = 'Testing';, yine de, bunun gibi bir şeyi yapan bir yerde bir bloğa ihtiyacım var:

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

İşin iyi yanı, tam sayılar / sihirli sayılarla uğraşmamaktır [bunun yerine, kodlanmış sabit miktarlarla uğraşmaktır], ama kötü olan şey, bu dönüşüm aşaması olmadan veritabanına girip çıkan şeyleri otomatik olarak sihirli bir şekilde çekememenizdir. bilgi.

Kısacası, "hala bir şeyleri kodlamak ve tamsayılarla uğraşmak zorunda olmak" gibi şeyler söyleyerek demek istedim. (Şimdi, Ocramius'un yorumundan sonra, tamsayılarla doğrudan uğraşmak zorunda kalmayacağım, ancak isimlendirilmiş sabitler ve gerektiğinde sabitlerden bazı dönüşümlere / dönüşümlerle uğraşmak zorunda kalmayacağım).


9
Ne yaparsan yap, yapma # 3. Onu koruyan psikopat sürekli bu sihirli sayıların ne anlama geldiğini bulmak zorunda kalacak. Bunu yaparsan, nerede yaşadığını bilmemelerini umsan iyi olur. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck

7
Seçenek 2'yi severim. Arama tablolarının çoğalmasını sevmiyorsanız, bir tablo kullanın ve bir "arama türü" sütunu ekleyin. Ancak evet, arama tablosu oluşturmak, bunu yapmanın "standart" yoludur, çünkü kullanıcı arayüzünde kolayca açılır pencere doldurmak gibi eğlenceli şeyler yapmanızı sağlar.
Robert Harvey,

Buradaki yayınlarınızda "EDIT" kullanmayın; biz bir forum değiliz. Her Stack Exchange gönderisi zaten herkesin görüntüleyebileceği ayrıntılı bir düzenleme geçmişi içeriyor .
Robert Harvey,

EDIT'i kullanamazsam ne kullanmalıyım?
Dennis,

Yazımı düzenleyin ve daha önce yaptığım gibi doğal görünmesini sağlayın. Değişiklikleri incelemek için düzenleme geçmişine bakın .
Robert Harvey

Yanıtlar:


35

Seçenek 2, referans tablolarını kullanarak, bunu yapmanın standart yoludur. Milyonlarca programcı tarafından kullanılmıştır ve çalıştığı bilinmektedir. Bu bir kalıptır , bu yüzden eşyalarına bakan başkaları ne olduğunu hemen anlar Veritabanları üzerinde çalışan, sizi çok ve çok sayıda işten kurtardığınız ve doğru şekilde idare edebileceğiniz kütüphaneler ve araçlar var. Bunu kullanmanın yararları sayısızdır.

Savurgan mı? Evet, ama sadece biraz. Herhangi bir yarı nezih veritabanı her zaman bu kadar sık ​​birleştirilen küçük masaları önbelleğe almaya devam edecektir, bu nedenle atık genellikle anlaşılmazdır.

Tanımladığınız diğer tüm seçenekler, MySQL'ler de dahil olmak üzere özel ve sahtedir enum; çünkü bu, SQL standardının bir parçası değildir. (Bundan başka, fikrimin enumkendisi değil, MySQL'in uygulanması neyin berbat olduğudur . Bir gün standardın bir parçası olarak görmeyi umursamıyorum.)

Düz bir tamsayı kullanarak # 3 olan son seçeneğiniz özellikle zararlıdır. Tüm dünyaların en kötüsünü elde edersiniz: referans bütünlüğü yok, adlandırılmış değer yok, değerin ne anlama geldiğinin veritabanında kesin bilgi yok, sadece her yere rastgele tamsayılar atılmış. Bu belirteçle, kodunuzdaki sabitleri kullanmayı bırakabilir ve bunun yerine sabit kodlanmış değerleri kullanmaya başlayabilirsiniz. circumference = radius * 6.28318530718;. Peki ya bu?

Referans tablolarını neden bu kadar zahmetli bulduğunuzu tekrar incelemelisiniz. Bildiğim kadarıyla, başka hiç kimse onları zor bulamaz. İş için doğru araçları kullanmadığınızdan olabilir mi?

"Bir şeyleri kodlamak ve tamsayılarla uğraşmak" veya "ayrıntılı programlama yapıları oluşturmak" veya "programlama tarafında yeni nesne yönelimli varlıklar yaratmak" konusundaki cümlesiniz, belki de nesne-ilişkisel yapmaya çalışabileceğinizi söylüyor Uygulamanızın kodu boyunca dağılmış olan sinek üzerinde haritalama (ORM) veya en iyi durumda, Hazırda Bekletme gibi mevcut bir ORM aracı kullanmak yerine, kendi nesne-ilişkisel eşleme mekanizmanızı yuvarlamaya çalışıyor olabilirsiniz. Bütün bunlar Hibernate ile bir esinti. Öğrenmesi biraz zaman alır, ancak bir kez öğrendikten sonra, uygulamanızı geliştirmeye gerçekten odaklanabilir ve veritabanında bir şeyi nasıl temsil edeceğiniz konusunda nitritli kumlama mekanizmasını unutabilirsiniz.

Son olarak, doğrudan veritabanıyla çalışırken hayatınızı kolaylaştırmak istiyorsanız, şu anda düşünebildiğim, yapabileceğiniz en az iki şey var:

  1. Ana tablolarınızı referans aldığı referans tablolarıyla birleştiren görünümler oluşturun, böylece her satır yalnızca referans kimliklerini değil, aynı zamanda ilgili adları da içerir.

  2. Referans tablo için bir tamsayı kimliği kullanmak yerine, 4 harfli kısaltmaları olan bir CHAR (4) sütunu kullanın. Böylece, kategorilerinizin kimlikleri "TEST", "DSGN", "PROG", "OTHR" olacaktı. (Onların açıklamaları elbette uygun İngilizce kelimeler olacaktır.) Biraz daha yavaş olacak, ama güven bana, kimse farketmeyecek.

Son olarak, sadece iki tür olduğunda çoğu insan yalnızca bir boolean sütunu kullanır. Böylece, bu "standart / istisna" sütunu bir boole olarak uygulanacak ve "IsException" olarak adlandırılacaktır.


3
Bir yana, Postgres'in de enum tipleri vardır . Basit ve özel bir şey değil, okunabilir bir dize değer olarak kullanmanıza izin verir, ancak kaputun altında daha verimli bir tamsayı kullanın.
Kat

Veriler art arda tekrarlandığında fakat gereksiz olmadığında (örneğin güncelleme / ekleme / silme anomalileriyle sonuçlanmayacaktır) durum ne olacak? Örneğin, bir kişinin cinsiyeti (yeni veri türlerini tanıma olasılığı düşük, hiçbir zaman bir cinsiyet adını vb. Değiştirmek zorunda kalmayacak)
Adam Thompson

Bu: çünkü sonunda bir “kabul ortamına” ihtiyacınız olduğunu ve değişmeyen enerjilerin değişmesi gerektiğini öğreneceksiniz.
Pieter B

3

Seçenek 2 programlama ucunda sabit veya numaralandırmalı.
Bilgiyi kopyalasa da, Tek Doğruluk Kaynağı ilkesini ihlal ederek, Fail-fast tekniğini kullanarak bununla başa çıkabilirsiniz . Sisteminiz yüklendiğinde, enums veya const değerlerinin veritabanında bulunduğunu kontrol eder. Değilse, sistem bir hata atmalı ve yüklemeyi reddetmelidir. Bu hatayı bu zamanda düzeltmek genellikle daha ciddi bir şey olmuş olabileceğinden daha ucuz olacaktır.


0

[Kısa] karakter dizilerini anahtar olarak kullanmanızı engelleyen hiçbir şey yoktur, bu nedenle yine de tablolarınızdaki adların okunabilirliğine sahip olabilir ve anlamsız vekil sayı kodlamasına başvuramazsınız. Hizmet Türlerini tanımlamak için ayrı bir tabloya sahip olmalısınız, örneğin, başvurunuz uluslararası olma ihtimaline karşı!

Kişisel Kullanıcılar da dört kategori görebilirsiniz kendi dilinde, ancak veritabanı tabloları hala değerleri içeren sen okuyabilir - ve hiçbiri herhangi bir veritabanı yapısı veya kod değişiklikler gerektirir!

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

veya Fransız müşterileriniz için ...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
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.