Bagaimana merancang tabel produk untuk berbagai jenis produk di mana setiap produk memiliki banyak parameter


140

Saya tidak punya banyak pengalaman dalam desain meja. Tujuan saya adalah membuat satu atau beberapa tabel produk yang memenuhi persyaratan di bawah ini:

  • Mendukung banyak jenis produk (TV, Telepon, PC, ...). Setiap jenis produk memiliki serangkaian parameter yang berbeda, seperti:

    • Telepon akan memiliki Warna, Ukuran, Berat, OS ...

    • PC akan memiliki CPU, HDD, RAM ...

  • Seperangkat parameter harus dinamis. Anda dapat menambah atau mengedit parameter apa pun yang Anda suka.

Bagaimana saya bisa memenuhi persyaratan ini tanpa tabel terpisah untuk setiap jenis produk?

Jawaban:


233

Anda memiliki setidaknya lima opsi ini untuk memodelkan hierarki tipe yang Anda uraikan:

  • Warisan Satu Tabel : satu tabel untuk semua jenis Produk, dengan kolom yang cukup untuk menyimpan semua atribut dari semua jenis. Ini berarti banyak kolom, yang sebagian besar NULL pada setiap baris yang diberikan.

  • Class Table Inheritance : satu tabel untuk Products, menyimpan atribut yang umum untuk semua jenis produk. Kemudian satu tabel per jenis produk, menyimpan atribut khusus untuk jenis produk itu.

  • Warisan Tabel Beton : tidak ada tabel untuk atribut Produk umum. Sebagai gantinya, satu tabel per jenis produk, menyimpan atribut produk umum, dan atribut spesifik produk.

  • LOB seri : Satu tabel untuk Produk, menyimpan atribut yang sama untuk semua jenis produk. Satu kolom tambahan menyimpan BLOB data semi-terstruktur, dalam format XML, YAML, JSON, atau lainnya. BLOB ini memungkinkan Anda untuk menyimpan atribut khusus untuk setiap jenis produk. Anda dapat menggunakan Pola Desain mewah untuk menggambarkan ini, seperti Fasad dan Memento. Tetapi terlepas dari Anda memiliki segumpal atribut yang tidak dapat dengan mudah ditanyakan dalam SQL; Anda harus mengambil seluruh gumpalan kembali ke aplikasi dan mengatasinya di sana.

  • Nilai Atribut-Atribut : Satu tabel untuk Produk, dan satu tabel yang memutar atribut ke baris, bukan kolom. EAV bukan desain yang valid sehubungan dengan paradigma relasional, tetapi banyak orang tetap menggunakannya. Ini adalah "Pola Properti" yang disebutkan oleh jawaban lain. Lihat pertanyaan lain dengan tag eav di StackOverflow untuk beberapa jebakan.

Saya telah menulis lebih banyak tentang ini dalam presentasi, Pemodelan Data yang Dapat Diperpanjang .


Pikiran tambahan tentang EAV: Meskipun banyak orang tampaknya menyukai EAV, saya tidak. Sepertinya solusi yang paling fleksibel, dan karena itu yang terbaik. Namun, perlu diingat pepatah TANSTAAFL . Berikut adalah beberapa kelemahan EAV:

  • Tidak ada cara untuk membuat kolom wajib (setara dengan NOT NULL).
  • Tidak ada cara untuk menggunakan tipe data SQL untuk memvalidasi entri.
  • Tidak ada cara untuk memastikan bahwa nama atribut dieja secara konsisten.
  • Tidak ada cara untuk meletakkan kunci asing pada nilai atribut yang diberikan, misalnya untuk tabel pencarian.
  • Mengambil hasil dalam tata letak tabel konvensional itu rumit dan mahal, karena untuk mendapatkan atribut dari beberapa baris yang perlu Anda lakukan JOINuntuk setiap atribut.

Tingkat fleksibilitas yang diberikan EAV Anda memerlukan pengorbanan di bidang lain, mungkin membuat kode Anda menjadi kompleks (atau lebih buruk) daripada menyelesaikan masalah asli dengan cara yang lebih konvensional.

Dan dalam kebanyakan kasus, tidak perlu memiliki tingkat fleksibilitas seperti itu. Dalam pertanyaan OP tentang jenis produk, jauh lebih mudah untuk membuat tabel per jenis produk untuk atribut spesifik produk, sehingga Anda memiliki beberapa struktur yang konsisten diberlakukan setidaknya untuk entri dari jenis produk yang sama.

Saya akan menggunakan EAV hanya jika setiap baris harus diizinkan memiliki serangkaian atribut yang berbeda. Ketika Anda memiliki serangkaian jenis produk yang terbatas, EAV berlebihan. Class Table Inheritance akan menjadi pilihan pertama saya.


Pembaruan 2019: Semakin saya melihat orang menggunakan JSON sebagai solusi untuk masalah "banyak atribut khusus", semakin saya tidak suka solusi itu. Itu membuat kueri terlalu rumit, bahkan ketika menggunakan fungsi JSON khusus untuk mendukungnya. Dibutuhkan lebih banyak ruang penyimpanan untuk menyimpan dokumen JSON, dibandingkan menyimpan dalam baris dan kolom normal.

Pada dasarnya, tidak ada solusi ini yang mudah atau efisien dalam database relasional. Seluruh gagasan memiliki "atribut variabel" pada dasarnya bertentangan dengan teori relasional.

Apa yang terjadi adalah Anda harus memilih salah satu solusi berdasarkan yang paling tidak buruk untuk aplikasi Anda . Karena itu Anda perlu tahu bagaimana Anda akan meminta data sebelum Anda memilih desain database. Tidak ada cara untuk memilih satu solusi yang "terbaik" karena salah satu solusi yang terbaik untuk aplikasi tertentu.


11
Opsi @HimalayaGarg "4.5" benar-benar kebalikan dari seluruh poin dari posting Bill.
user3308043

2
Tidak seperti MySQL, SQL Server memiliki dukungan luas untuk XML, XPath dan XQuery. Jadi untuk pengguna SQL Server, opsi terbaik adalah menyimpan atribut tambahan dalam kolom tipe XML (opsi 4). Dengan cara ini Anda TIDAK harus "mengambil seluruh gumpalan kembali ke aplikasi dan mengatasinya di sana." Anda bahkan dapat membuat indeks pada kolom XML di SQL Server.
Delphi.Boy


2
Saya lebih suka LOB seri untuk kasus saya. Tetapi apakah ini cocok untuk ORM? Saya menggunakan EF.
Mahmood Jenami

@ user2741577, tentu saja, tetapi Anda mungkin harus menulis kode khusus untuk membongkar bidang data yang tidak terstruktur membentuk LOB dan menerapkannya ke setiap bidang entitas objek ORM Anda. Saya tidak tahu EF, tapi saya kira Anda bisa membuat kelas ORM dasar yang melakukan ini. Anda perlu melacak bidang mana yang berasal dari bidang beton dari baris basis data, dan bidang mana yang berasal dari bidang LOB, sehingga Anda dapat membentuk kembali LOB saat saatnya untuk menyimpan objek.
Bill Karwin

12

@StoneHeart

Saya akan pergi di sini dengan EAV dan MVC sepanjang jalan.

@ Bill Karvin

Berikut adalah beberapa kelemahan EAV:

  • Tidak ada cara untuk membuat kolom wajib (setara dengan NOT NULL).
  • Tidak ada cara untuk menggunakan tipe data SQL untuk memvalidasi entri.
  • Tidak ada cara untuk memastikan bahwa nama atribut dieja secara konsisten.
  • Tidak ada cara untuk meletakkan kunci asing pada nilai atribut yang diberikan, misalnya untuk tabel pencarian.

Semua hal yang telah Anda sebutkan di sini:

  • validasi data
  • validasi nama atribut ejaan
  • kolom / bidang wajib
  • menangani penghancuran atribut dependen

menurut pendapat saya tidak termasuk dalam database sama sekali karena tidak ada database yang mampu menangani interaksi dan persyaratan pada tingkat yang tepat seperti bahasa pemrograman aplikasi.

Menurut pendapat saya menggunakan database dengan cara ini seperti menggunakan batu untuk memalu paku. Anda dapat melakukannya dengan batu tetapi bukankah Anda seharusnya menggunakan palu yang lebih tepat dan dirancang khusus untuk kegiatan semacam ini?

Mengambil hasil dalam tata letak tabel konvensional itu rumit dan mahal, karena untuk mendapatkan atribut dari banyak baris, Anda perlu BERGABUNG untuk setiap atribut.

Masalah ini dapat diselesaikan dengan membuat beberapa pertanyaan tentang data parsial dan memprosesnya menjadi tata letak tabel dengan aplikasi Anda. Bahkan jika Anda memiliki 600GB data produk, Anda dapat memprosesnya dalam batch jika Anda memerlukan data dari setiap baris dalam tabel ini.

Menuju lebih jauh Jika Anda ingin meningkatkan kinerja kueri, Anda dapat memilih operasi tertentu seperti untuk mis. Pelaporan atau pencarian teks global dan mempersiapkan mereka tabel indeks yang akan menyimpan data yang diperlukan dan akan dibuat ulang secara berkala, katakanlah setiap 30 menit.

Anda bahkan tidak perlu khawatir dengan biaya penyimpanan data tambahan karena semakin murah dan lebih murah setiap hari.

Jika Anda masih khawatir dengan kinerja operasi yang dilakukan oleh aplikasi, Anda selalu dapat menggunakan Erlang, C ++, Go Language untuk melakukan pra-proses data dan kemudian hanya memproses data yang dioptimalkan lebih lanjut di aplikasi utama Anda.


you can always use Erlang, C++, Go Language to pre-process the dataApa yang kamu maksud? Alih-alih DB, gunakan Go lang? Bisakah Anda menjelaskannya?
Hijau

1
Saya sangat setuju. EAV adalah cara untuk maju, terutama jika Anda membutuhkan tingkat fleksibilitas yang akan memungkinkan Anda untuk menambahkan jenis produk dan parameter baru tanpa perubahan skema db, maksud saya hidup dalam produksi melalui aplikasi Anda. Pernah ke sana, melakukan itu. Bekerja untukku. Tentang pertanyaan lambat ... apakah ada orang di sini yang pernah mendengar tentang cache? ;)
pawel.kalisz

@Green Saya telah mengedit paragraf terakhir untuk membuatnya lebih jelas, tetapi ini tentang meneruskan data EAV mentah Anda ke proses dalam bahasa yang dapat menangani transformasi data, pencarian dalam struktur pohon atau peta dasar apa pun yang mengurangi operasi dengan sangat cepat dan dengan cara yang efisien memori. Spesifik di sini akan tergantung pada apa yang perlu dioptimalkan
Pawel Barcik

6

Jika saya menggunakan Class Table Inheritancemakna:

satu tabel untuk Produk, menyimpan atribut yang umum untuk semua jenis produk. Kemudian satu tabel per jenis produk, menyimpan atribut khusus untuk jenis produk itu. -Bill Karwin

Yang saya suka yang terbaik dari Saran Bill Karwin .. Saya bisa meramalkan satu kelemahan, yang akan saya coba jelaskan bagaimana menjaga agar tidak menjadi masalah.

Apa rencana darurat yang harus saya miliki ketika atribut yang hanya umum untuk 1 jenis, kemudian menjadi umum untuk 2, kemudian 3, dll?

Misalnya: (ini hanya contoh, bukan masalah saya yang sebenarnya)

Jika kita menjual furnitur, kita mungkin menjual kursi, lampu, sofa, TV, dll. Jenis TV mungkin merupakan satu-satunya jenis yang kita bawa yang memiliki konsumsi daya. Jadi saya akan meletakkan power_consumptionatribut di tv_type_table. Tetapi kemudian kita mulai membawa sistem Home theater yang juga memiliki power_consumptionproperti. OK itu hanya satu produk lain jadi saya akan menambahkan bidang ini stereo_type_tablejuga karena itu mungkin paling mudah saat ini. Tetapi seiring berjalannya waktu kita mulai membawa lebih banyak dan lebih banyak barang elektronik, kita menyadari bahwa power_consumptionitu cukup luas sehingga seharusnya ada di dalamnya main_product_table. Apa yang harus saya lakukan sekarang?

Tambahkan bidang ke main_product_table. Tulis skrip untuk mengulang melalui elektronik dan menempatkan nilai yang benar dari masing type_table- masing ke main_product_table. Lalu jatuhkan kolom itu dari masing-masing type_table.

Sekarang Jika saya selalu menggunakan GetProductDatakelas yang sama untuk berinteraksi dengan database untuk menarik info produk; maka jika ada perubahan dalam kode sekarang perlu refactoring, mereka harus ke Kelas itu saja.


3

Anda dapat memiliki tabel Produk dan tabel ProductAdditionInfo yang terpisah dengan 3 kolom: ID produk, nama info tambahan, nilai info tambahan. Jika warna digunakan oleh banyak tetapi tidak semua jenis Produk Anda bisa memilikinya menjadi kolom nullable di tabel Produk, atau cukup taruh di ProductAdditionalInfo.

Pendekatan ini bukan teknik tradisional untuk database relasional, tetapi saya telah melihatnya banyak digunakan dalam praktik. Itu bisa fleksibel dan memiliki kinerja yang baik.

Steve Yegge menyebutnya pola Properti dan menulis posting panjang tentang menggunakannya.


4
Pola Properti hanyalah Entitas-Atribut-Nilai dengan nama lain. Ini digunakan secara luas, tetapi menyimpannya dalam basis data relasional melanggar aturan normalisasi.
Bill Karwin

2
Sejujurnya, ketika saya membaca deskripsi EAV di @Bills menjawab saya tidak begitu mengerti apa yang dia jelaskan. Tetapi ketika Anda mengatakan 3 columns: product ID, additional info name, additional info valuesaya mengerti konsep itu. Dan saya benar-benar melakukan ini sebelumnya, dan mengalami masalah. Namun, saya tidak ingat pada saat itu apa masalah itu.
JD Isaacks

1
@JDIsaacks Dalam pola ini, masalah umum adalah bahwa kita tidak tahu berapa banyak GABUNGAN yang kita perlukan untuk mengambil semua atribut.
Omid
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.