Bagaimana Anda mendesain basis data pengguna dengan bidang khusus

18

Pertanyaan ini seputar bagaimana saya harus mendesain database, bisa berupa database relasional / nosql, tergantung pada apa yang akan menjadi solusi yang lebih baik


Diberi persyaratan di mana Anda harus membuat sistem yang akan melibatkan basis data untuk melacak "Perusahaan" dan "Pengguna". Satu pengguna selalu hanya milik satu perusahaan

  • Seorang pengguna hanya dapat menjadi bagian dari satu perusahaan
  • Perusahaan dapat memiliki banyak pengguna

Desain untuk tabel "Perusahaan" cukup mudah. Perusahaan akan memiliki atribut / kolom berikut: (mari kita tetap sederhana)

ID, COMPANY_NAME, CREATED_ON

Skenario pertama

Sederhana & lurus ke depan, semua pengguna memiliki atribut yang sama, jadi ini dapat dengan mudah dilakukan dalam gaya relasional, tabel pengguna:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Skenario kedua

Apa yang terjadi jika perusahaan yang berbeda ingin menyimpan atribut profil yang berbeda untuk pengguna mereka. Setiap perusahaan akan memiliki seperangkat atribut yang ditentukan yang akan berlaku untuk semua pengguna perusahaan itu.

Sebagai contoh:

  • Perusahaan A ingin menyimpan: LIKE_MOVIE (boolean), LIKE_MUSIC (boolean)
  • Perusahaan B ingin menyimpan: FAV_CUISINE (String)
  • Perusahaan C ingin menyimpan: OWN_DOG (boolean), DOG_COUNT (int)

Pendekatan 1

cara brute force adalah memiliki skema tunggal untuk pengguna dan membiarkan mereka memiliki nol ketika mereka bukan milik perusahaan:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, LIKE_MOVIE, LIKE_MUSIC, FAV_CUISINE, OWN_DOG, DOG_COUNT, CREATED_ON

Yang agak jahat karena Anda akan berakhir dengan banyak NULLS dan baris pengguna yang memiliki kolom yang tidak relevan dengan mereka (mis. Semua pengguna milik Perusahaan A memiliki nilai NULL untuk FAV_CUISINE, OWN_DOG, DOG_COUNT)

Pendekatan 2

pendekatan kedua, adalah memiliki "bidang bentuk bebas":

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_1, CUSTOM_2, CUSTOM_3, CREATED_ON

Yang tidak menyenangkan karena Anda sendiri tidak tahu bidang khusus apa, tipe data tidak akan mencerminkan nilai yang disimpan (mis. Kami akan menyimpan nilai int sebagai VARCHAR).

Pendekatan 3

Saya telah melihat ke dalam bidang JSON PostgreSQL, dalam hal ini Anda akan memiliki:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_PROFILE_JSON, CREATED_ON

Dalam hal ini, bagaimana Anda bisa menerapkan skema yang berbeda untuk pengguna? Seorang pengguna dengan Perusahaan A akan memiliki skema yang terlihat seperti

 {"LIKE_MOVIE":"boolean", "LIKE_MUSIC": "boolean"}

Sementara pengguna dengan Perusahaan C akan memiliki skema berbeda:

 {"OWN_DOG ":"boolean", "DOG_COUNT": "int"}

Bagaimana saya mengatasi masalah ini? Bagaimana saya bisa mendesain database dengan benar untuk memungkinkan skema fleksibel ini untuk satu "objek" (Pengguna) berdasarkan hubungan yang mereka miliki (Perusahaan)?

solusi relasional? solusi nosql?


Sunting: Saya juga memikirkan tabel "CUSTOM_PROFILE" yang pada dasarnya akan menyimpan atribut pengguna dalam baris daripada kolom.

Ada 2 masalah dengan pendekatan ini:

1) Data tumbuh per pengguna tumbuh sebagai baris daripada kolom - dan ini berarti untuk mendapatkan gambaran lengkap dari pengguna, banyak gabungan yang harus dilakukan, beberapa bergabung ke tabel "profil khusus" pada atribut khusus yang berbeda

2) Nilai data selalu disimpan sebagai VARCHAR untuk menjadi generik, bahkan jika kita tahu data itu seharusnya bilangan bulat atau boolean dll

noobcser
sumber
3
Jika perusahaan yang berbeda memiliki set data multi-nilai yang berbeda pada setiap pelanggan, maka Anda benar-benar membutuhkan tabel penautan COMPANY_CUSTOMER. Segala sesuatu yang lain akan menyebabkan Anda sangat sakit segera.
Kilian Foth
Bagaimana tabel tautan membantu dengan data khusus? kolom masih harus berbeda
noobcser
1
Anda harus menunjukkan fakta "Kata sandi Kilian untuk IKEA 'kucing'" dengan tuple seperti "PERUSAHAAN: IKEA, PELANGGAN: Kilian, ATRIBUT: kata sandi, VALUE: kitten". Apa pun yang lebih sederhana tidak akan berhasil.
Kilian Foth
3
Skema adalah hal yang tetap, menurut definisi; Anda tidak dapat mengaturnya jika Anda tidak tahu bidang apa yang Anda butuhkan. Lihatlah Entity-Attribute-Value untuk masalah satu arah seperti ini cenderung diselesaikan dalam database relasional.
Mason Wheeler

Jawaban:

13

Harap pertimbangkan ini sebagai alternatif. Dua contoh sebelumnya akan mengharuskan Anda membuat perubahan pada skema saat ruang lingkup aplikasi bertambah selain solusi "custom_column" sulit untuk diperluas dan dipelihara. Akhirnya, Anda akan berakhir dengan Custom_510 dan bayangkan betapa buruknya tabel ini.

Pertama, mari kita gunakan skema Perusahaan Anda.

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

Selanjutnya kami juga akan menggunakan skema Pengguna Anda untuk atribut tingkat atas yang diperlukan yang akan digunakan / dibagikan oleh semua perusahaan.

[Users] UserId, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Selanjutnya kita membuat tabel di mana kita akan mendefinisikan atribut dinamis kita yang spesifik untuk setiap atribut pengguna khusus perusahaan. Jadi di sini nilai contoh kolom Atribut adalah "LikeMusic":

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

Selanjutnya kita mendefinisikan tabel UserAttributes yang akan menyimpan nilai atribut pengguna

[UserAttributes] UserAttributeDefinitionId, UserId, Value

Ini dapat dimodifikasi dengan berbagai cara untuk menjadi lebih baik untuk kinerja. Anda bisa menggunakan beberapa tabel untuk UserAttributes yang membuat masing-masing spesifik untuk tipe data yang disimpan di Value atau biarkan saja sebagai VarChar dan bekerja dengannya sebagai store nilai kunci.

Anda juga mungkin ingin memindahkan CompanyId dari tabel UserAttributeDefiniton dan ke tabel referensi silang untuk pemeriksaan selanjutnya.

P. Roe
sumber
terima kasih - saya pikir tentang pendekatan seperti itu - silakan lihat edit. 2 masalah: 1) Data tumbuh sebagai baris, yang berarti untuk mendapatkan gambaran lengkap dari pengguna, Anda harus melakukan banyak penggabungan. 2) "nilai" akan selalu disimpan sebagai VARCHAR untuk menjadi generik, bahkan jika nilainya sebenarnya int atau boolean dll
noobcser
1
Jika Anda menggunakan int / bigint untuk identitas tabel dan bergabung dengan orang-orang yang Anda tidak akan memiliki masalah kinerja sampai Anda berada di sejumlah baris. Sekarang jika Anda mulai mencari berdasarkan nilai atribut ini bisa menimbulkan masalah jika Anda mulai mendapatkan sejumlah besar catatan. Dalam hal ini saya akan bekerja dengan DBA untuk menentukan apakah ada indeks yang dapat dibuat atau mungkin tampilan yang diindeks yang dapat mempercepat pencarian semacam ini. Saya telah menggunakan skema serupa dan dibutuhkan dalam 100 juta catatan setahun tanpa masalah kinerja apa pun sehingga desain dasar bekerja dengan cukup baik IMO
P. Roe
Jika pelaporan, pemfilteran, permintaan diperlukan dan atribut yang berbeda mungkin milik set data yang berbeda. Apakah pendekatan ini lebih baik daripada NoSQL? Saya mencoba memahami perbedaan kinerja. Situasi serupa hanya pengguna yang dapat menentukan laporan yang berisi bidang yang ditentukan pengguna.
kos
Dalam pendekatan di atas, bagaimana kita mengimplementasikan pencarian, seperti diff. perusahaan ingin mencari di bidangnya, termasuk bidang pengguna juga. Apa pendekatan yang tepat untuk memberikan pencarian yang scalable di atas ini
techagrammer
Anda dapat mencarinya secara normal dengan banyak gabungan. Anda dapat menggunakan skrip ETL untuk mengekstrak data yang ingin Anda cari dan menempatkannya dalam struktur yang lebih terdenormalisasi. Terakhir Anda dapat mencoba memanfaatkan tampilan yang diindeks sebagai metode untuk mencari. Secara pribadi saya merekomendasikan metode ETL untuk menghasilkan struktur denormalized yang mudah dicari.
P. Roe
7

Gunakan database NoSQL. Akan ada dokumen perusahaan dan pengguna. Pengguna akan memiliki bagian dari skema mereka yang dibuat secara dinamis berdasarkan pada templat pengguna (teks untuk menunjukkan bidang / jenis untuk perusahaan itu.

\Company\<uniqueidentifier>
    - Name: <Name>
    - CreatedOn: <datetime>
    - UserTemplate: <Text>

\User\<uniqueidentifier>
    - COMPANY_ID: <ID>
    - FIRST_NAME: <Text>
    - LAST_NAME: <Text>
    - EMAIL: <Text>
    - CREATED_ON: <datetime>
    - * Dynamically created fields per company

Ini adalah tampilannya di Firebase.com. Anda harus belajar melakukannya di mana pun yang Anda pilih.

JeffO
sumber
ini adalah apa yang saya pikirkan atau mungkin kolom JSON. Bagaimana kinerja permintaan, pelaporan penyaringan dibandingkan dengan solusi yang diajukan oleh PRO.
kos
1
Setiap kali Anda mengompres data ke json atau xml dan kemudian melemparkannya ke dalam kolom, itu akan sangat lambat untuk dicari. Jika Anda perlu mencari data yang disajikan dalam jawaban saya di atas maka saya akan menyarankan menggunakan tampilan yang diindeks untuk mengambil data. Jika solusi itu tidak ideal maka saya akan merekomendasikan menggunakan ETL untuk menyalin data ke dalam struktur yang dapat dengan mudah dicari dan dilaporkan.
P. Roe
Dalam pendekatan di atas, bagaimana kita mengimplementasikan pencarian, seperti diff. perusahaan ingin mencari di bidangnya, termasuk bidang pengguna juga. Apa pendekatan yang tepat untuk memberikan pencarian yang scalable di atas ini
techagrammer
Dalam database nosql, Anda mungkin memiliki data yang berlebihan, tetapi terstruktur dengan cara yang dapat dicari. Yang ditunjukkan di atas adalah dengan pengidentifikasi unik. Yang lain bisa menjadi \ Perusahaan \ Nama. Ini mirip dengan memiliki beberapa indeks.
JeffO
3

Jika Anda sering mengalami permintaan bidang khusus, saya akan memodelkannya dengan database. Buat tabel yang menyimpan metadata tentang setiap bidang kustom, CompanyCustomField (milik siapa, tipe data, dll.) Dan tabel lain CompanyCustomFieldValues ​​yang berisi CustomerId, FieldId, dan nilainya. Jika Anda menggunakan sesuatu seperti Microsoft Sql Server, saya akan memiliki kolom nilai menjadi tipe data sql_variant.

Tentu saja ini tidak mudah karena Anda akan membutuhkan antarmuka yang memungkinkan admin menentukan bidang khusus untuk setiap pelanggan, dan antarmuka lain yang benar-benar menggunakan metadata ini untuk membangun UI untuk mengumpulkan nilai-nilai bidang. Dan jika Anda memiliki persyaratan lain, seperti pengelompokan bidang bersama-sama atau kebutuhan untuk melakukan jenis daftar pilih bidang Anda harus mengakomodasi itu dengan lebih banyak metadata / tabel lain (misalnya, CompanyCustomFieldPickListOptions).

Ini bukan hal sepele, tetapi memiliki keuntungan karena tidak memerlukan perubahan database / perubahan kode untuk setiap bidang kustom baru. Fitur lain dari bidang khusus perlu dikodekan juga (misalnya, jika Anda ingin regex memvalidasi nilai string, atau hanya mengizinkan tanggal antara rentang tertentu, atau jika Anda perlu mengaktifkan satu bidang khusus berdasarkan pada nilai bidang khusus lainnya ).

Andy
sumber
terima kasih - saya pikir tentang pendekatan seperti itu - silakan lihat edit. 2 masalah: 1) Data tumbuh sebagai baris, yang berarti untuk mendapatkan gambaran lengkap dari pengguna, Anda harus melakukan banyak penggabungan. 2) "nilai" akan selalu disimpan sebagai VARCHAR untuk menjadi generik, bahkan jika nilainya sebenarnya int atau boolean dll
noobcser
1
@noobcser Data yang tumbuh sebagai baris tidak terlalu penting, setelah semua database dirancang di sekitar baris dan bergabung. Dalam hal apa pun Anda lebih cenderung menggunakan Common Table Expressions untuk ini yang cukup bagus dalam hal semacam ini. Saya tidak yakin apakah Anda melewatkan bagian di mana saya katakan Anda dapat menggunakan sql_variant sebagai tipe data untuk kolom nilai, yang menyimpan nilai sebagai tipe apa pun yang Anda tempel di dalamnya. Sementara saya menamai nama fitur MS SQL server, saya berharap DBMS dewasa lainnya memiliki fitur serupa.
Andy
1
@noobcser FYI Saya benar-benar mengalami persyaratan ini cukup sering dalam karir saya dan memiliki pengalaman dengan masing-masing solusi yang diusulkan, jadi saya menyarankan salah satu yang paling berhasil dalam pengalaman saya. Penggunaan tipe data xml untuk hal semacam ini adalah sebagian mengapa saya benci MS menambahkan xml sebagai tipe data asli.
Andy
1

Alternatif untuk jawaban lain adalah memiliki tabel yang disebut profile_attrib, atau serupa dengan skema yang sepenuhnya dikelola oleh aplikasi Anda.

Saat atribut khusus ditambahkan, Anda ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1), Anda bisa melarang menghapusnya. Ini akan meminimalkan bergabung Anda, sambil tetap memberikan fleksibilitas.

Saya kira bit trade-off adalah aplikasi sekarang perlu mengubah hak istimewa tabel ke database, dan Anda harus pintar dalam membersihkan nama kolom.

Chris Seufert
sumber
Ekspresi reguler [^\w-]+seharusnya cukup baik melakukannya, tidak membiarkan apa pun tidak - 0-9A-Za-z_-tetapi ya, sanitasi di sini adalah suatu keharusan untuk melindungi dari kejahatan atau kebodohan.
Reguler Joe
0

Pertanyaan Anda memiliki banyak solusi potensial. Salah satu solusinya adalah menyimpan attribrutes tambahan sebagai XML. XML dapat disimpan sebagai teks atau jika Anda menggunakan database yang mendukung tipe XML sebagai XML (SQL Server). Menyimpan sebagai teks membatasi kemampuan permintaan Anda (seperti mencari pada atribut khusus), tetapi jika menyimpan dan mengambil adalah semua yang Anda butuhkan maka itu adalah solusi yang bagus. Jika seseorang perlu bertanya, maka menyimpan XML sebagai tipe XML akan menjadi pilihan yang lebih baik (meskipun ini lebih spesifik untuk vendor).

Ini akan memberi seseorang kemampuan untuk menyimpan sejumlah atribut ke pelanggan hanya dengan menambahkan kolom tambahan pada tabel pelanggan. Seseorang dapat menyimpan atribut sebagai hashset atau kamus, seseorang akan kehilangan keamanan jenis karena semuanya akan menjadi string untuk memulai, tetapi jika seseorang memaksakan string format standar untuk tanggal, angka, boolean, itu akan berhasil OK.

Untuk informasi lebih lanjut:

https://msdn.microsoft.com/en-us/library/hh403385.aspx

Jawaban @ WalterMitty juga berlaku, meskipun jika seseorang memiliki banyak pelanggan dengan atribut yang berbeda, seseorang dapat berakhir dengan banyak tabel jika mengikuti model pewarisan. Itu tergantung pada berapa banyak atribut khusus dibagi di antara pelanggan.

Jon Raynor
sumber
Ini dapat bekerja juga, tetapi saya merasa menjadi terbatas setelah Anda benar-benar perlu melakukan sesuatu terhadap data yang disimpan di bidang XML / JSON.
Andy
@Andy - Benar, ada lapisan lain. Permintaan DB dan parsing XML sebagai lawan dari permintaan DB saja. Saya tidak tahu apakah saya akan menyebutnya membatasi, hanya lebih rumit. Tapi, itu akan menjadi sesuatu yang perlu dipertimbangkan jika atribut khusus digunakan secara luas.
Jon Raynor
Dalam T-SQL dimungkinkan untuk mendefinisikan konten di kolom XML / JSON terhadap namespace dan permintaan terhadap elemen pada data kustom. Itu tidak sulit
Stephen York
-1

Anda harus menormalkan basis data Anda sehingga Anda memiliki 3 tabel berbeda untuk setiap jenis profil perusahaan. Menggunakan contoh Anda, Anda akan memiliki tabel dengan kolom:

USER_ID, LIKE_MOVIE, LIKE_MUSIC

USER_ID, FAVORITE_CUISINE

USER_ID, OWN_DOG, DOG_COUNT

Pendekatan ini mengasumsikan bahwa Anda akan mengetahui bentuk informasi yang ingin disimpan oleh perusahaan sebelumnya dan tidak akan sering berubah. Jika bentuk data tidak diketahui pada waktu desain, mungkin akan lebih baik untuk pergi dengan bidang JSON itu atau database nosql.

mortalapeman
sumber
-1

Untuk satu alasan atau yang lain, database adalah satu bidang di mana efek platform bagian dalam paling sering muncul. Ini hanyalah kasus anti-pola yang muncul.

Dalam hal ini, Anda mencoba melawan solusi alami dan benar. Pengguna Perusahaan A bukan pengguna Perusahaan B, dan mereka harus memiliki tabel sendiri untuk bidang mereka sendiri.

Vendor basis data Anda tidak membebani Anda dengan tabel, dan Anda tidak perlu dua kali ruang disk untuk dua kali tabel (pada kenyataannya, memiliki dua tabel lebih efisien karena Anda tidak menyimpan atribut A untuk pengguna B. Bahkan menyimpan hanya NULLs membutuhkan ruang).

Tentu saja, jika ada bidang umum yang cukup, Anda dapat memasukkannya ke dalam tabel Pengguna bersama, dan memiliki kunci asing di setiap tabel pengguna khusus perusahaan. Ini adalah struktur yang sangat sederhana sehingga tidak ada pengoptimal kueri basis data yang berjuang dengannya. GABUNGAN yang diperlukan sepele.

MSalters
sumber
3
Dan jika Anda memiliki ribuan pelanggan, sebuah tabel per masing-masing dapat dengan cepat menjadi tidak dapat dipelihara, belum lagi Anda akan membutuhkan kode khusus untuk bidang khusus masing-masing pelanggan.
Andy
@Andy: Coba tebak? Situasi akan semakin tidak dapat dipelihara jika Anda mencampur seribu skema berbeda ke dalam satu tabel! Dan ya, Anda mungkin perlu kode khusus untuk bidang khusus. Sekali lagi itu lebih sederhana, tidak sulit, jika setiap pelanggan memiliki meja yang bersih dan terpisah. Mencoba mengambil bidang perusahaan X dari ribuan lainnya adalah kekacauan berdarah.
MSalters
Apakah Anda merujuk pada jawaban saya atau ide OPs yang menyematkan semua kolom tambahan ke tabel pelanggan?
Andy
2
Tujuannya di sini adalah untuk menemukan solusi yang dapat dipertahankan & terukur. Membuat tabel per pelanggan jelas merupakan kebalikan dari itu. Setiap kali Anda mendapatkan pelanggan baru, tidaklah realistis untuk: menjalankan skrip buat tabel, memperbarui kode Anda (objek Entitas), dan menggunakan kembali.
tsOverflow
Seluruh ide menggunakan tabel bersama untuk semua pelanggan itu sendiri merupakan diskusi arsitektur SaaS yang terpisah, dan ada beberapa alasan bagus untuk menjaga pelanggan di tabel yang berbeda (atau bahkan dalam database yang berbeda, memungkinkan pencadangan / pengembalian dan pengembalian skala per pelanggan). Dalam skenario ini, membuat kolom cusotm di tabel utama adalah no-brainer. Saya membenarkan, dan saya bertanya-tanya mengapa orang downvote ini hanya karena mereka tidak menyukai pendekatan ini. Efek platform bagian dalam adalah kenyataan: dengan menggunakan model EVA, kueri Anda akan lebih sulit, lebih hemat, integritas lebih keras, dll.
drizin
-1

Solusi saya berasumsi bahwa Anda akan memanggil kueri ini dari suatu program dan Anda harus dapat melakukan pemrosesan posting. Anda dapat memiliki kolom berikut:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_VALUES

CUSTOM_VALUES akan berupa kunci penyimpanan string dan pasangan nilai. kunci akan menjadi nama kolom dan nilai akan menjadi nilai kolom misalnya

LIKE_MOVIE;yes;LIKE_MUSIC;no;FAV_CUISINE;rice

dalam CUSTOM_VALUES ini Anda hanya akan menyimpan informasi yang ada. Saat Anda kueri dari program, Anda dapat memisahkan string ini dan menggunakannya.

Saya telah menggunakan logika ini dan berfungsi dengan baik, hanya saja Anda harus menerapkan logika penyaringan dalam kode dan bukan dalam kueri.

techExplorer
sumber