Apakah boros membuat tabel database baru alih-alih menggunakan tipe data enum?

38

Misalkan saya memiliki 4 jenis layanan yang saya tawarkan (mereka tidak mungkin sering berubah):

  • Pengujian
  • Desain
  • Pemrograman
  • Lain

Misalkan saya memiliki 60-80 layanan aktual yang masing-masing termasuk dalam salah satu kategori di atas. Misalnya, 'layanan' dapat berupa "Program Uji menggunakan teknik A" dan merupakan jenis "Pengujian".

Saya ingin menyandikannya ke dalam basis data. Saya datang dengan beberapa opsi:

Opsi 0:

Gunakan VARCHARlangsung untuk menyandikan tipe layanan secara langsung sebagai string

Pilihan 1:

Gunakan basis data enum. Tapi enum itu jahat

Pilihan 2:

gunakan dua tabel:

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

Saya bahkan dapat menikmati integritas referensial:

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

Kedengarannya bagus, ya?

Tapi saya masih harus mengkodekan hal-hal dan berurusan dengan bilangan bulat, yaitu saat mengisi tabel. Atau saya harus membuat pemrograman rumit atau konstruksi DB saat mengisi atau berurusan dengan tabel. Yaitu, BERGABUNG ketika berurusan dengan database secara langsung, atau membuat entitas berorientasi objek baru di sisi pemrograman, dan memastikan saya mengoperasikannya dengan benar.

Opsi 3:

Jangan gunakan enum, jangan gunakan dua tabel, tetapi cukup gunakan kolom integer

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

Ini seperti 'enum palsu' yang membutuhkan lebih banyak overhead pada sisi kode hal-hal, seperti mengetahui itu {2 == 'Programming'}dan menghadapinya dengan tepat.

Pertanyaan:

Saat ini saya telah menerapkannya menggunakan Opsi 2 , dipandu dengan konsep

  1. jangan gunakan enum (opsi 1)
  2. hindari menggunakan database sebagai spreadsheet (opsi 0)

Tetapi saya tidak dapat menahan perasaan yang tampaknya sia-sia bagi saya dalam hal pemrograman dan overhead kognitif - saya harus menyadari dua tabel, dan berurusan dengan dua tabel, vs satu.

Untuk 'cara yang kurang boros', saya melihat Option 3. TI lebih ringan dan pada dasarnya memerlukan konstruksi kode yang sama untuk beroperasi (dengan sedikit modifikasi tetapi kompleksitas dan struktur pada dasarnya sama tetapi dengan satu tabel)

Saya kira idealnya tidak selalu sia-sia, dan ada kasus yang baik untuk kedua opsi, tetapi apakah ada pedoman yang baik tentang kapan seseorang harus menggunakan Opsi 2 dan kapan Opsi 3?

Ketika hanya ada dua jenis (biner)

Untuk menambahkan sedikit lebih banyak ke pertanyaan ini ... di tempat yang sama, saya memiliki opsi biner dari Layanan "Standar" atau "Pengecualian", yang dapat diterapkan pada item baris layanan. Saya telah menyandikannya menggunakan Opsi 3 .

Saya memilih untuk tidak membuat tabel baru hanya untuk menyimpan nilai {"Standard", "Exception"}. Jadi kolom saya hanya menampung {0, 1} dan nama kolom saya dipanggil exception, dan kode saya sedang melakukan terjemahan dari {0, 1} => {STANDARD, EXCEPTION}(yang saya encode sebagai konstanta dalam bahasa pemrograman)

Sejauh ini tidak menyukai cara itu juga ..... (tidak menyukai opsi 2 atau opsi 3). Saya memang menemukan opsi 2 lebih unggul dari 3, tetapi dengan lebih banyak overhead, dan masih saya tidak dapat melarikan diri pengkodean hal-hal sebagai bilangan bulat tidak peduli opsi mana yang saya gunakan dari 2, dan 3.

ORM

Untuk menambahkan beberapa konteks, setelah membaca jawaban - Saya baru saja mulai menggunakan ORM lagi (baru-baru ini), dalam kasus saya Doctrine 2. Setelah mendefinisikan skema DB melalui Annotations, saya ingin mengisi basis data. Karena seluruh kumpulan data saya relatif kecil, saya ingin mencoba menggunakan konstruksi pemrograman untuk melihat cara kerjanya.

Saya pertama kali mengisi service_types, dan kemudian service_line_items, karena ada daftar yang ada dari spreadsheet yang sebenarnya. Jadi hal-hal seperti 'standar / pengecualian' dan 'Pengujian' semuanya adalah string pada spreadsheet, dan mereka harus dikodekan ke dalam tipe yang tepat sebelum menyimpannya dalam DB.

Saya menemukan jawaban SO ini: Apa yang Anda gunakan daripada ENUM di doctrine2? , yang menyarankan untuk tidak menggunakan enum construct DB, tetapi untuk menggunakan INTbidang dan untuk mengkodekan tipe menggunakan konstruk 'const' dari bahasa pemrograman.

Tetapi seperti yang ditunjukkan dalam pertanyaan SO di atas, saya dapat menghindari menggunakan bilangan bulat secara langsung dan menggunakan konstruksi bahasa - konstanta - setelah mereka didefinisikan ....

Tapi tetap saja .... tidak peduli bagaimana Anda mengubahnya, jika saya mulai dengan stringsebagai tipe, saya harus terlebih dahulu mengubahnya menjadi tipe yang tepat, bahkan ketika menggunakan ORM.

Jadi jika katakan $str = 'Testing';, saya masih perlu memiliki blok di suatu tempat yang melakukan sesuatu seperti:

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

Hal yang baik adalah Anda tidak berurusan dengan bilangan bulat / angka ajaib [sebagai gantinya, berurusan dengan jumlah konstan yang disandikan], tetapi hal buruknya adalah Anda tidak dapat secara otomatis menarik masuk dan keluar dari database tanpa langkah konversi ini, menurut saya pengetahuan.

Dan itulah yang saya maksud, sebagian, dengan mengatakan hal-hal seperti "masih harus menyandikan hal-hal dan berurusan dengan bilangan bulat". (Memang, sekarang, setelah komentar Ocramius, saya tidak perlu berurusan langsung dengan bilangan bulat, tetapi berurusan dengan konstanta bernama dan beberapa konversi ke / dari konstanta, sesuai kebutuhan).

Dennis
sumber
9
Apa pun yang Anda lakukan, jangan lakukan # 3. Psikopat yang mempertahankannya harus mencari tahu apa arti angka-angka ajaib itu. Jika Anda melakukan itu, Anda lebih baik berharap mereka tidak tahu di mana Anda tinggal. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck
7
Saya suka Opsi 2. Jika Anda tidak suka proliferasi tabel pencarian, gunakan satu tabel dan tambahkan kolom "tipe pencarian". Tapi ya, membuat tabel pencarian adalah cara "standar" untuk melakukan ini, karena memungkinkan Anda untuk melakukan hal-hal menyenangkan seperti dengan mudah mengisi dropdown di UI.
Robert Harvey
Jangan gunakan "EDIT" di posting Anda di sini; kami bukan forum. Setiap posting Stack Exchange sudah berisi riwayat edit terperinci yang dapat dilihat siapa pun.
Robert Harvey
jika saya tidak dapat menggunakan EDIT, apa yang harus saya gunakan?
Dennis
Cukup edit posting dan membuatnya terlihat alami, seperti yang sudah saya lakukan. Lihat riwayat edit untuk meninjau perubahan.
Robert Harvey

Jawaban:

35

Opsi # 2, menggunakan tabel referensi, adalah cara standar untuk melakukannya. Ini telah digunakan oleh jutaan programmer, dan diketahui bekerja. Ini adalah pola , jadi siapa pun yang melihat barang-barang Anda akan segera tahu apa yang sedang terjadi. Ada pustaka dan alat yang berfungsi pada basis data, menyelamatkan Anda dari banyak dan banyak pekerjaan, yang akan menanganinya dengan benar. Manfaat menggunakannya tidak terhitung.

Apakah itu boros? Ya, tetapi hanya sedikit. Setiap basis data yang setengah layak akan selalu menyimpan tabel-tabel kecil yang sering bergabung, sehingga limbahnya biasanya tidak terlihat.

Semua opsi lain yang Anda jelaskan ad hoc dan hacky, termasuk MySQL enum, karena itu bukan bagian dari standar SQL. (Selain itu, yang menyebalkan enumadalah implementasi MySQL, bukan ide itu sendiri. Saya tidak keberatan melihatnya suatu hari sebagai bagian dari standar.)

Opsi terakhir Anda # 3 dengan menggunakan bilangan bulat biasa terutama hacky. Anda mendapatkan yang terburuk dari semua dunia: tidak ada integritas referensial, tidak ada nilai-nilai yang disebutkan, tidak ada pengetahuan definitif dalam database tentang apa arti suatu nilai, hanya bilangan bulat sewenang-wenang yang dilemparkan ke semua tempat. Dengan token ini, Anda mungkin juga berhenti menggunakan konstanta dalam kode Anda, dan mulai menggunakan nilai-nilai hard-coded. circumference = radius * 6.28318530718;. Bagaimana tentang itu?

Saya pikir Anda harus memeriksa kembali mengapa Anda menemukan tabel referensi memberatkan. Tidak ada yang menemukan mereka berat, sejauh yang saya tahu. Mungkinkah itu karena Anda tidak menggunakan alat yang tepat untuk pekerjaan itu?

Kalimat Anda tentang harus "menyandikan hal-hal dan berurusan dengan bilangan bulat", atau harus "membuat konstruksi pemrograman rumit", atau "membuat entitas berorientasi objek baru di sisi pemrograman", memberi tahu saya bahwa mungkin Anda mungkin mencoba melakukan objek-relasional pemetaan (ORM) dengan cepat tersebar di seluruh kode aplikasi Anda, atau dalam kasus terbaik Anda mungkin mencoba untuk menggulung mekanisme pemetaan objek-relasional Anda sendiri, alih-alih menggunakan alat ORM yang ada untuk pekerjaan itu, seperti Hibernate. Semua hal ini sangat mudah dengan Hibernate. Butuh sedikit waktu untuk mempelajarinya, tetapi setelah Anda mempelajarinya, Anda dapat benar-benar fokus mengembangkan aplikasi Anda dan melupakan mekanisme rumit tentang cara merepresentasikan barang di database.

Akhirnya, jika Anda ingin membuat hidup Anda lebih mudah ketika bekerja secara langsung dengan database, setidaknya ada dua hal yang dapat Anda lakukan, yang dapat saya pikirkan saat ini:

  1. Buat tampilan yang bergabung dengan tabel utama Anda dengan tabel referensi apa pun yang mereka rujuk, sehingga setiap baris tidak hanya berisi id referensi, tetapi juga nama yang sesuai.

  2. Alih-alih menggunakan id integer untuk tabel referensi, gunakan kolom CHAR (4), dengan singkatan 4 huruf. Jadi, id dari kategori Anda akan menjadi "TEST", "DSGN", "PROG", "OTHR". ( Deskripsi mereka akan tetap kata-kata bahasa Inggris yang tepat, tentu saja.) Ini akan sedikit lebih lambat, tapi percayalah, tidak ada yang akan memperhatikan.

Akhirnya, ketika hanya ada dua jenis, kebanyakan orang hanya menggunakan kolom boolean. Jadi, kolom "standar / pengecualian" akan diimplementasikan sebagai boolean dan itu akan disebut "IsException".

Mike Nakis
sumber
3
Selain itu, Postgres juga memiliki tipe enum . Mereka sederhana dan tidak ada yang istimewa, memungkinkan Anda untuk menggunakan string yang dapat dibaca sebagai nilai, tetapi memiliki integer yang lebih efisien digunakan di bawah tenda.
Kat
Bagaimana dengan kasus ketika data diulangi secara konsekuen, tetapi tidak berlebihan (mis. Tidak akan menghasilkan anomali pembaruan / penyisipan / penghapusan)? Misalnya, jenis kelamin seseorang (tidak mungkin untuk memperkenalkan tipe data baru, tidak perlu mengubah nama jenis kelamin, dll.)
Adam Thompson
Ini: karena pada akhirnya Anda akan mengetahui bahwa Anda memerlukan "lingkungan penerimaan" dan enum Anda yang tidak berubah perlu diubah.
Pieter B
3

Opsi 2 dengan konstanta atau enum di akhir pemrograman.
Meskipun duplikat pengetahuan, melanggar prinsip Single Source Of Truth, Anda dapat mengatasinya dengan menggunakan teknik Gagal-cepat . Ketika sistem Anda memuatnya akan memeriksa bahwa nilai-nilai enums atau const ada dalam database. Jika tidak, sistem harus melakukan kesalahan dan menolak memuat. Biasanya akan lebih murah untuk memperbaiki bug ini saat ini daripada nanti ketika sesuatu yang lebih serius mungkin terjadi.

José Margaça Lopes
sumber
0

Tidak ada yang menghentikan Anda menggunakan string [pendek] sebagai kunci, sehingga Anda masih bisa memiliki keterbacaan nama di tabel Anda dan tidak menggunakan penyandian angka pengganti yang tidak berarti. Anda masih harus memiliki tabel terpisah untuk menggambarkan Jenis Layanan, hanya jika ada kemungkinan, katakanlah, aplikasi Anda menjadi internasional!

Pengguna Anda dapat melihat empat kategori Anda dalam bahasa mereka sendiri , tetapi tabel basis data Anda masih mengandung nilai-nilai yang dapat Anda baca - dan tidak ada satupun yang membutuhkan struktur basis data atau perubahan kode!

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         |
+-------------+----------------+

atau, untuk pelanggan Prancis Anda ...

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'; 
Phill W.
sumber