Apakah praktik yang buruk untuk mengizinkan bidang yang ditentukan pengguna?

17

Secara umum, apakah dianggap praktik yang buruk untuk mengizinkan bidang yang dibuat pengguna dalam database untuk aplikasi web?

Sebagai contoh, saya membuat webapp inventaris rumah untuk istri saya, dan dia akan ingin mendefinisikan bidangnya sendiri untuk item yang berbeda. Saya berencana untuk mengizinkannya membuat kategori item, dan menambahkan "fitur" ke kategori tersebut. Fitur hanya akan menjadi kunci / nilai yang disimpan sebagai string. Dengan begitu jika dia memiliki kategori yang disebut "Audio CD" misalnya, dia bisa menambahkan fitur untuk hal-hal seperti "artis", "trek", dll. Tetapi dalam kategori lain seperti "furnitur", dia bisa menambahkan fitur untuk barang-barang seperti "bahan" "(kayu, plastik, dll). Maka item apa pun bisa menjadi milik satu (atau banyak) kategori, menambahkan fitur-fitur itu ke item.

Saya dapat melihat masalah di mana mencari dengan fitur-fitur ini membutuhkan perbandingan string, tidak ada validasi data, dll. Mengikuti metodologi tangkas, mungkin akan lebih baik jika dia datang dengan kategori dan atribut baru dan saya hanya harus membuat tabel baru saat kita pergi. Dalam contoh saya, ini adalah basis pengguna kecil (2 dari kami) dan jumlah catatan yang dibuat akan kecil, jadi tidak terlalu buruk.

Namun secara umum, bagaimana orang menangani hal seperti ini dalam "kehidupan nyata"?

zako42
sumber
4
Sudahkah Anda mempertimbangkan untuk menggunakan database berorientasi dokumen seperti MongoDB? Anda bisa menyimpan dokumen per jenis yang bertindak sebagai skema yang juga dapat diedit (mungkin secara manual, mengingat skala kecil proyek).
Andy Hunt
@AndyBursh salah satu bit 'fun' dengan postgres saat ini adalah tipe data 'json' ( tautan ). Pendekatan semacam itu akan memungkinkan seseorang untuk menyimpan bidang yang ditentukan pengguna dalam data itu, eh, mendokumentasikan, eh, apa pun dan kemudian menggunakan seluruh bidang untuk hal-hal yang Anda indeks dan sejenisnya. Meskipun ini semua tergantung pada penggunaan dan sulit untuk mengatakan apakah ini akan bekerja dengan baik untuk aplikasi tertentu atau tidak. Tapi itu sesuatu yang harus diperhatikan.
semua: diskusi yang hebat, terima kasih untuk semua wawasannya! @AndyBursh Saya pernah mendengar tentang MongoDB tetapi tidak pernah benar-benar membacanya. Kedengarannya seperti proyek rumah lain untuk bereksperimen dengan ...
zako42

Jawaban:

19

Ketika Anda mulai mendapatkan "bidang yang ditentukan pengguna" seperti yang sering ditemukan di pelacak bug, manajemen sumber daya pelanggan, dan alat bisnis serupa adalah bahwa mereka tidak didukung dengan tabel dengan bidang bajillion (jika ya, maka kemungkinan masalah itu sendiri).

Alih-alih apa yang Anda temukan adalah desain tabel Nilai Atribut Entitas dan alat administrasi terkait untuk mengelola atribut yang valid.

Pertimbangkan tabel berikut:

  + -------------- +
  | hal |
  | -------------- |
  | id |
  | ketik |
  | desc |
  | attr1 |
  | attr2 |
  | attr3 |
  | attr4 |
  | attr5 |
  + -------------- +

Ini setelah Anda menambahkan beberapa atribut. Alih-alih attr1berpura - pura membaca artistatau tracksatau genreatribut apa pun yang dimiliki benda itu. Dan bukannya 5, bagaimana jika itu 50. Jelas itu tidak bisa diatur. Ini juga memerlukan pembaruan model dan pemindahan aplikasi untuk menangani bidang baru. Tidak ideal

Sekarang perhatikan struktur tabel berikut:

  + -------------- + + --------------- + + ------------- +
  | hal | | thing_attr | | attr |
  | -------------- | | --------------- | | ------------- |
  | id | <--- + | thing_id (fk) | +> | id |
  | ketik | | attr_id (fk) | + - + | nama |
  | desc | | nilai | | |
  + -------------- + + --------------- + + ------------- +

Anda mendapatkan barang Anda dengan bidang dasarnya. Anda memiliki dua tabel lagi. Satu dengan atribut. Setiap bidang adalah baris dalam attrtabel. Dan kemudian ada thing_attrdengan sepasang kunci asing yang berkaitan kembali ke thingmeja dan attrmeja. Dan ini kemudian memiliki bidang nilai tempat Anda menyimpan nilai bidang apa pun untuk entitas itu nantinya.

Dan sekarang Anda punya struktur di mana tabel attr dapat diperbarui saat runtime dan bidang baru dapat ditambahkan (atau dihapus) dengan cepat tanpa dampak signifikan pada keseluruhan aplikasi.

Pertanyaannya sedikit lebih kompleks dan validasi menjadi lebih kompleks juga (baik prosedur tersimpan yang funky atau semua sisi klien). Ini adalah trade off dalam desain.

Pertimbangkan juga situasi di mana suatu hari Anda perlu melakukan migrasi dan Anda kembali ke aplikasi untuk menemukan bahwa sekarang ada setengah lusin atribut atau lebih dari skema yang Anda bagikan pada awalnya. Ini membuat migrasi dan pemutakhiran yang buruk di mana tabel Nilai Atribut Entitas, bila digunakan dengan benar, bisa lebih bersih. (Tidak selalu, tetapi bisa.)


Apakah ada kerugian untuk hanya memodifikasi skema saat runtime? Jika pengguna berpikir suatu hal memerlukan atribut baru, cukup tambahkan kolom secara dinamis ke tabel?

Jika Anda bekerja dengan citarasa yang sesuai dari database nosql, Anda mungkin bisa melakukan ini (perhatikan bahwa citarasa yang tepat dari nosql untuk ini mungkin akan menjadi toko nilai kunci yang, yah, tabel EAV untuk yang berhubungan seperti dijelaskan di atas) tanpa terlalu banyak kesulitan. Namun ia datang dengan semua kompromi untuk nosql yang dijelaskan di tempat lain dengan sangat rinci.

Jika Anda bekerja pada basis data relasional - Anda harus memiliki skema. Menambahkan kolom secara dinamis berarti beberapa bagian dari hal-hal berikut ini benar:

  • Anda sedang melakukan pemrograman meta-database. Alih-alih dapat memetakan kolom ini dengan bersih ke bidang itu dengan ORM yang bagus, Anda mungkin melakukan hal-hal seperti select *dan kemudian melakukan beberapa kode kompleks untuk mengetahui apa sebenarnya data tersebut (lihat ResultSetMetaData Java ) dan kemudian menyimpannya di peta ( atau tipe data lain - tetapi bidang yang tidak bagus dalam kode). Ini kemudian membuang sedikit jenis dan keamanan kesalahan ketik yang Anda miliki dengan pendekatan tradisional.
  • Anda kemungkinan telah meninggalkan ORM. Ini berarti Anda menulis sql mentah untuk semua kode alih-alih membiarkan sistem bekerja untuk Anda.
  • Anda sudah menyerah melakukan peningkatan bersih. Apa yang terjadi ketika pelanggan menambahkan bidang dengan satu nama yang juga digunakan oleh versi Anda selanjutnya? Di situs perjodohan, pemutakhiran yang ingin menambahkan hasdatebidang untuk menyimpan stempel waktu telah didefinisikan sebagai hasdateboolean untuk kecocokan yang berhasil ... dan pemutakhiran Anda rusak.
  • Anda percaya bahwa pelanggan tidak merusak sistem dengan menggunakan kata khusus yang merusak permintaan Anda juga ... di suatu tempat.
  • Anda mengikat diri Anda pada satu merek basis data. The DDL dari database yang berbeda berbeda. Jenis basis data adalah contoh termudah dari ini. varchar2vs textdan sejenisnya. Kode Anda untuk menambahkan kolom akan berfungsi pada MySQL tetapi tidak pada Postgres atau Oracle atau SQL Server.
  • Apakah Anda percaya pelanggan benar-benar menambahkan data dengan baik ? Tentu, EAV jauh dari ideal tetapi sekarang Anda punya beberapa nama tabel yang tidak dikenal yang tidak ditambahkan oleh pengembang Anda, dengan jenis indeks yang salah (jika ada), tanpa kendala ditambahkan dalam kode di mana perlu menjadi dan seterusnya.
  • Anda telah memberikan hak istimewa skema modifikasi kepada pengguna yang menjalankan aplikasi. Little Bobby Drop Tables tidak mungkin ketika Anda dibatasi untuk SQL daripada DDL (tentu saja Anda dapat melakukannya delete * from students, tetapi Anda tidak dapat mengacaukan database dengan cara yang buruk). Jumlah hal yang bisa salah dengan akses skema baik dari kecelakaan atau meroketnya aktivitas jahat.

Ini benar-benar bermuara pada "jangan lakukan itu." Jika Anda benar-benar menginginkan ini, lakukan dengan pola struktur tabel EAV yang diketahui atau database yang sepenuhnya didedikasikan untuk struktur ini. Jangan biarkan orang membuat bidang sembarang dalam tabel. Sakit kepala tidak layak.

DougM
sumber
4
Anda juga telah menemukan kembali basis data.
user253751
1
@immibis telah menambahkan lapisan di mana pengguna dapat mengelola tanpa mengacaukan seluruh database atau memerlukan pengerahan kembali untuk memperbarui model.
1
@immibis EAV telah diperdebatkan dengan hangat di lingkaran basis data relasional selama bertahun-tahun. Secara teori, itu tidak perlu, tetapi dalam praktiknya, Anda tidak dapat melakukan hal-hal tertentu tanpanya.
Ross Patterson
1
@ ShivanDragon yang masuk ke pendekatan NoSQL. Toko dokumen hanya menyimpan dokumen dan tidak memaksakan skema. Dengan demikian, menambah dan menghapus bidang dan parsing dokumen sepenuhnya di luar lingkup database itu sendiri (dan Anda telah menulis model Anda untuk mengakomodasi itu). Ini adalah kompromi yang sama sekali berbeda dari kompromi database relasional untuk struktur EAV.
5

Melakukan ini dengan baik sulit.

Untuk aplikasi satu kali seperti yang Anda rencanakan, Anda tentu saja dapat menambahkan kolom untuk setiap bidang, dan menyediakan UI yang membuat definisi bidang oleh pengguna yang tidak terlatih lebih aman daripada memberi mereka baris perintah SQL. Atau Anda dapat mengikuti pola Entity-Attribute-Value yang ditakuti , yang merupakan respons klasik, jika agak menakutkan, terhadap masalah semacam ini. Membangun UI untuk mendefinisikan bidang EAV biasanya jauh lebih kompleks daripada untuk kolom basis data, dan kueri bisa menjadi sangat berbulu, tetapi untuk sejumlah besar bidang ( yaitu , skema matriks sangat jarang), itu mungkin satu-satunya cara untuk mendapatkan pekerjaan selesai.

Ross Patterson
sumber
Singkatnya: proyek kecil == KISS. Agile ke tanah.
Encaitar
Masalah dengan pembaruan tabel database, adalah bahwa tergantung pada jumlah data dan indeks yang diperlukan (bidang khusus sering memerlukan fitur pencarian), tabel yang mengubah kueri dapat memakan waktu sangat lama. Singkatnya adalah bahwa MySQL dan database relasional lainnya bukan media yang baik untuk persyaratan semacam ini.
Oddman
0

Saya datang salib beberapa hal serupa baru-baru ini.

Saya membuat 2 tabel.

1: table Objects 
    Id , name, type

Dia adalah semua objekmu. U mengatur nama itu.

Dan jenis objek ini: - bagi saya jenis yang tersedia adalah inventaris, inventory_item, kantor.

Dan setup yang biasa adalah n item adalah child atau inventory yang juga child of office dan saya menggunakan tabel join untuk menggabungkan objek satu sama lain

2 table settings 
     organization_Id , title, value , type

Tabel pengaturan berisi setiap nama bidang untuk jenis objek tertentu, dan nilai nilainya.

Contoh properti kantor

Lokasi, telepon, jam kerja

Dan untuk barang-barang

  • Jumlah
  • Harga
  • Kode batang

Dll, semua properti ini diberlakukan oleh model Anda dan disimpan dalam tabel pengaturan sebagai baris terpisah (namun gunakan ganti tidak disisipkan untuk menghindari beberapa baris untuk bidang yang sama)

Jadi kapan pun saya ingin kantor saya memuatnya dengan mudah dengan semua relasi dan pengaturannya di mana pengaturan object_I'd masuk (objek yang diminta)

Setelah itu saya memutar semua baris dari pengaturan dan hanya itu.

Dan jika saya ingin pengaturan menjadi spesifik untuk item dalam inventaris (bukan global) saya atur object_I'd = Saya akan dari tabel hubungan object_objects dan saya atur setting.type = relation_setting

Saya harap Anda mengerti apa yang saya maksud, saya akan mencoba memformat ulang jawaban ketika saya mendapatkan laptop

Zalaboza
sumber
2
Kiat pro - jangan memposting di forum ini dari ponsel Anda. Koreksi otomatis membuat bagian dari posting Anda tidak dapat dibaca.
BobDalgleish
Pengamatan bagus
haha
0

Apakah praktik yang buruk untuk mengizinkan bidang yang ditentukan pengguna?

Tidak, ini bukan praktik buruk. Ini cukup umum. Dalam istilah OO ini disebut pewarisan. Anda memiliki inventaris item kelas dasar dan dua kelas bawaan AudioCD dan furnitur.

Namun secara umum, bagaimana orang menangani hal seperti ini dalam "kehidupan nyata"?

Anda harus memutuskan bagaimana inventaris barang, AudioCD, dan furnitur disimpan dalam basis data.

Jika easy-query paling penting bagi Anda dan db-space / normalisasi tidak masalah Anda akan mengimplementasikan skema "table-per-hierarchy".

Jika ruang / normalisasi paling penting bagi Anda dan pertanyaan yang lebih rumit tidak masalah bagi Anda untuk mengimplementasikan Skema "table-per-type".

Untuk lebih jelasnya lihat dotnet table-per-type-vs-table-per-hierarchy-inheritance atau java hibernate inheritance .

k3b
sumber
Saya tidak tahu apakah ini menjawab pertanyaan itu. Pengguna tidak mengubah kode untuk membuat kelas baru
Colin D