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 VARCHAR
langsung 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
- jangan gunakan enum (opsi 1)
- 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_type
s, dan kemudian service_line_item
s, 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 INT
bidang 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 string
sebagai 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).
Jawaban:
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 menyebalkanenum
adalah 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:
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.
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".
sumber
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.
sumber
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!
atau, untuk pelanggan Prancis Anda ...
sumber