Як би ви створили базу даних користувачів зі спеціальними полями


18

Це питання полягає в тому, як я повинен створити базу даних, це можуть бути реляційні / nosql бази даних, залежно від того, що буде кращим рішенням


З огляду на вимогу, де потрібно створити систему, яка буде включати базу даних для відстеження "Компанія" та "Користувач". Один користувач завжди належить лише одній компанії

  • Користувач може належати лише одній компанії
  • У компанії може бути багато користувачів

Дизайн столу «Компанія» досить простий. Компанія матиме такі атрибути / стовпці: (давайте нехай це буде просто)

ID, COMPANY_NAME, CREATED_ON

Перший сценарій

Прості та прямі, всі користувачі мають однаковий атрибут, тому це можна легко зробити у реляційному стилі, таблиця користувачів:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CREATED_ON

Другий сценарій

Що станеться, якщо різні компанії хочуть зберігати різні атрибути профілю для свого користувача. Кожна компанія матиме визначений набір атрибутів, який би застосовувався до всіх користувачів цієї компанії.

Наприклад:

  • Компанія A хоче зберігати: LIKE_MOVIE (булева), LIKE_MUSIC (булева)
  • Компанія B хоче зберігати: FAV_CUISINE (String)
  • Компанія C хоче зберігати: OWN_DOG (булева), DOG_COUNT (int)

Підхід 1

найсуворішим способом є створення єдиної схеми для користувача та нехай вони мають нулі, коли вони не належать компанії:

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

Що ніби неприємно, оскільки у вас буде багато NULLS та рядків користувачів, у яких стовпці не мають відношення до них (тобто всі користувачі, що належать до компанії A, мають NULL значення для FAV_CUISINE, OWN_DOG, DOG_COUNT)

Підхід 2

другий підхід - це "поле вільної форми":

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

Що було б неприємно самостійно, оскільки ви не маєте поняття, що таке власні поля, тип даних не відображатиме значення, що зберігаються (наприклад, ми будемо зберігати значення int як VARCHAR).

Підхід 3

Я переглянув поле JSON PostgreSQL, і в цьому випадку у вас буде:

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

Як у цьому випадку ви могли б застосувати різні схеми до користувача? Користувач з компанією A матиме подібну схему

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

Хоча користувач із компанією C матиме іншу схему:

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

Як я повинен вирішити це питання? Як я можу правильно розробити базу даних, щоб дозволити цю гнучку схему для одного "об'єкта" (Користувача) на основі відносин, які вони мають (Компанія)?

реляційне рішення? nosql розчин?


Редагувати: Я також думав про таблицю "CUSTOM_PROFILE", яка по суті буде зберігати атрибути користувача в рядках, а не в стовпцях.

З цим підходом є дві проблеми:

1) Дані зростають на кожного користувача, зростаючи як рядки, а не стовпці - і це означає, щоб отримати повну картину користувача, потрібно зробити багато приєднань, кілька приєднань до таблиці "спеціальний профіль" на різних спеціальних атрибутах

2) Значення даних завжди зберігається як VARCHAR, щоб бути загальним, навіть якщо ми знаємо, що дані повинні бути цілими чи булевими тощо.


3
Якщо різні компанії мають різні, багатозначні набори даних про кожного клієнта, то вам абсолютно потрібна таблиця зв’язку COMPANY_CUSTOMER. Все інше дуже швидко заподіє вам великий біль.
Кіліан Фот

Як допоможе таблиця зв’язування з користувацькими даними? стовпці все одно повинні бути іншими
noobcser

1
Ви повинні представити факт "Пароль Кіліана для IKEA -" кошеня "з кортежем типу" КОМПАНІЯ: ІКЕА, КЛІЄНТ: Кіліан, АТРИБУТ: пароль, ЦІННІСТЬ: кошеня ". Все простіше не виконає роботу.
Кіліан Фот

3
Схема - це фіксована річ за визначенням; ви не можете налаштувати його, якщо ви не знаєте, які поля вам потрібні. Погляньте на значення Entity-Attribute-Value, щоб вирішити такі проблеми, як ця, як правило, у реляційній базі даних.
Мейсон Уілер

Відповіді:


13

Розгляньте це як альтернативу. Попередні два приклади вимагають внести зміни в схему, оскільки область застосування додатка зростає, крім того, рішення "custom_column" складно розширити і підтримувати. Згодом ви опинитеся з Custom_510, а потім лише уявіть, як жахливо буде працювати ця таблиця.

Спершу давайте скористаємося схемою ваших компаній.

[Companies] ComnpanyId, COMPANY_NAME, CREATED_ON

Далі ми також будемо використовувати вашу схему користувачів для необхідних атрибутів верхнього рівня, які будуть використовуватися / ділитися всіма компаніями.

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

Далі ми будуємо таблицю, де будемо визначати наші динамічні атрибути, характерні для кожної компанії, атрибути користувача. Отже, тут прикладом значення стовпця "Атрибут" буде "LikeMusic":

[UserAttributeDefinition] UserAttributeDefinitionId, CompanyId, Attribute

Далі ми визначаємо таблицю UserAttributes, яка буде містити значення атрибутів користувача

[UserAttributes] UserAttributeDefinitionId, UserId, Value

Це можна змінити багатьма способами, щоб покращити продуктивність. Ви можете використовувати кілька таблиць для UserAttributes, роблячи кожну конкретну для типу даних, що зберігаються у Value, або просто залишити її як VarChar та працювати з нею як сховище ключових значень.

Ви також можете перенести CompanyId з таблиці UserAttributeDefiniton та перехресну таблицю для подальшого підтвердження.


дякую - я хоч про такий підхід - будь ласка, дивіться редагувати. 2 проблеми: 1) Дані зростають у вигляді рядків, що означає, щоб отримати повну картину користувача, вам доведеться зробити багато приєднань. 2) "значення" завжди зберігатиметься як VARCHAR, щоб бути загальним, навіть якщо значення насправді є
цілим

1
Якщо ви використовуєте int / bigint для ідентифікацій таблиці та приєднуєтесь до тих, у кого ви не будете мати проблем із продуктивністю, поки ви не будете мати надзвичайну кількість рядків. Тепер, якщо ви почнете пошук на основі значень атрибутів, це може створити проблему, якщо ви почнете отримувати величезну кількість записів. У цьому випадку я працюю з DBA, щоб визначити, чи можуть бути створені індекси або, можливо, індексований вигляд, який може прискорити подібні пошуки. Я використовував подібну схему, і вона займає 100 мільйонів записів на рік без жодних питань щодо продуктивності, тому базовий дизайн працює досить добре IMO
P. Roe

Якщо потрібно звітувати, фільтрувати, запитувати, і різні атрибути можуть належати до різних наборів даних. Чи був би такий підхід кращим, ніж NoSQL? Я намагаюся зрозуміти різницю в продуктивності. Подібна ситуація лише користувач може визначати звіти, які містять визначені користувачем поля.
kos

У вищенаведеному підході, як ми реалізуємо пошукову річ, як це відрізняється. компанії хочуть шукати на своїх полях, включаючи і поля користувачів. Який правильний підхід для забезпечення масштабованого пошуку на вершині цього
techagrammer

Ви можете звичайно шукати його з великою кількістю приєднань. Ви можете використовувати сценарій ETL для вилучення даних, які ви хочете шукати, та розміщення їх у більш денормалізованій структурі. Нарешті, ви можете спробувати використовувати індексовані представлення як метод пошуку. Особисто я рекомендую метод ETL для створення денормалізованих структур, які легко шукати.
P. Roe

7

Використовуйте базу даних NoSQL. Були б документи компанії та користувача. Користувачі мали б частину своєї схеми динамічно створювати на основі шаблону користувача (текст із зазначенням полів / типів для цієї компанії.

\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

Ось як це може виглядати щось на зразок Firebase.com. Вам доведеться навчитися робити це в будь-якому обраному вами.


це те, про що я думаю, або, можливо, колонки JSON. Яка ефективність запитів, фільтрації звітів порівняно з рішенням, запропонованим PRoe.
kos

1
Щоразу, коли ви стискаєте дані в json або xml, а потім кидаєте їх у стовпець, пошук буде дуже повільно. Якщо вам потрібно шукати дані, представлені в моїй відповіді вище, я б радив використовувати індексовані представлення для отримання даних. Якщо це рішення не є ідеальним, я рекомендую використовувати ETL для копіювання даних у структуру, за якою можна легко шукати та повідомляти.
P. Roe

У вищенаведеному підході, як ми реалізуємо пошукову річ, як це відрізняється. компанії хочуть шукати на своїх полях, включаючи і поля користувачів. Який правильний підхід для забезпечення масштабованого пошуку на вершині цього
techagrammer

У базі даних nosql ви можете мати зайві дані, але вони структуровані таким чином, щоб їх можна було шукати. Показаний вище - це унікальний ідентифікатор. Ще одним може бути \ Компанія \ Назва. Це схоже на наявність декількох індексів.
JeffO

3

Якщо ви часто збираєтесь зіштовхуватися зі спеціальними запитами на місцях, я насправді моделюю це приблизно аналогічно базі даних. Створіть таблицю, яка містить метадані про кожне користувацьке поле, CompanyCustomField (кому належить, тип даних тощо) та іншу таблицю CompanyCustomFieldValues, яка містить CustomerId, FieldId та значення. Якщо ви використовуєте щось на зразок сервера Microsoft Sql, я б сказав, що стовпець значення буде типом даних sql_variant.

Звичайно, це непросто, оскільки вам знадобиться інтерфейс, який дозволяє адміністраторам визначати спеціальні поля для кожного клієнта, а також інший інтерфейс, який фактично використовує ці метадані для створення інтерфейсу для збору значень поля. І якщо у вас є інші вимоги, такі як згрупування полів разом або необхідність складати поле списку вибору, вам потрібно буде відповідати цьому більше метаданих / інших таблиць (наприклад, CompanyCustomFieldPickListOptions).

Це не тривіально, але має перевагу не вимагати змін бази даних / змін коду для кожного нового спеціального поля. Будь-які інші функції спеціальних полів також повинні бути закодовані (наприклад, якщо ви хочете повторно виразити валідацію значення рядка, або дозволити лише дати між певними діапазонами, або якщо вам потрібно включити одне користувацьке поле на основі іншого значення спеціального поля ).


дякую - я хоч про такий підхід - будь ласка, дивіться редагувати. 2 проблеми: 1) Дані зростають у вигляді рядків, що означає, щоб отримати повну картину користувача, вам доведеться зробити багато приєднань. 2) "значення" завжди зберігатиметься як VARCHAR, щоб бути загальним, навіть якщо значення насправді є
цілим

1
@noobcser Дані, що ростуть у вигляді рядків, насправді не мають значення, адже всі бази даних проектуються навколо рядків і об'єднуються. У будь-якому випадку ви, швидше за все, для цього використовуєте загальні табличні вирази, які дуже добре вживають подібних речей. Я не впевнений, якщо ви пропустили ту частину, де я сказав, що ви можете використовувати sql_variant як тип даних для стовпця значень, який зберігає значення, як би ви не ввійшли до нього. Хоча я називаю імена функцій сервера MS SQL, я очікую, що інші зрілі СУБД мають подібні функції.
Енді

1
@noobcser FYI Насправді я часто зустрічався з цими вимогами у своїй кар’єрі і маю досвід роботи з кожним із запропонованих рішень, тому я пропоную той, який найкраще працював у моєму досвіді. Використання типів даних xml для подібних речей частково є причиною того, що я ненавиджу цей MS, який додає xml як нативний тип даних.
Енді

1

Альтернативою іншим відповідям є створення таблиці під назвою profile_attrib або подібної до неї схемою повністю керувати ваша програма.

Як додані спеціальні атрибути, ви ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1) , ви можете заборонити їх видаляти. Це дозволить мінімізувати ваше приєднання, одночасно надаючи гнучкість.

Я думаю, що дефіцит компромісу - це додаток, який зараз потребує змін до привілеїв таблиці до бази даних, і ви повинні бути розумними щодо оздоровлення імен стовпців.


Звичайний вираз [^\w-]+повинен дуже добре це робити, не допускаючи нічого, що це не 0-9A-Za-z_-- але так, санітарія тут необхідна для захисту від зловмисності чи дурості.
Регулярний Джо

0

Ваше запитання має багато потенційних рішень. Одне рішення - зберігати додаткові атрибути як XML. XML може зберігатися у вигляді тексту або якщо ви використовуєте базу даних, яка підтримує типи XML як XML (SQL Server). Збереження тексту обмежує вашу можливість запиту (наприклад, пошук за спеціальним атрибутом), але якщо зберігання та пошук - це все, що вам потрібно, то це гарне рішення. Якщо потрібно здійснити запит, кращим варіантом буде зберігання XML як типу XML (хоча це стосується більше постачальника).

Це дасть можливість зберігати будь-яку кількість атрибутів клієнту, лише додаючи стовпчик додавання на таблиці клієнтів. Отримати атрибути можна як хеш-набір або словник, ви втратите безпеку типу, оскільки все буде рядком для початку, але якщо застосувати стандартний рядок формату для дат, чисел, булевих, він вийде добре.

Для отримання додаткової інформації:

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

@ Відповідь WalterMitty також справедлива, хоча якщо у вас є багато клієнтів з різними атрибутами, ви можете отримати багато таблиць, якщо слідувати моделі успадкування. Це залежить від того, скільки спеціальних атрибутів поділяються між клієнтами.


Це може також працювати, але я відчуваю, що стає обмеженим, коли вам потрібно щось робити проти даних, що зберігаються в полі XML / JSON.
Енді

@Andy - Правда, є ще один шар. Запит БД та розбір XML на відміну від просто БД запитів. Я не знаю, чи назвав би це обмежуючим, просто більш громіздким. Але було б щось врахувати, якщо користувацькі атрибути широко використовувались.
Джон Рейнор

У T-SQL можна визначити вміст у стовпці XML / JSON щодо простору імен та запиту проти елементів на користувацьких даних. Це не складно
Стівен Йорк

-1

Ви повинні нормалізувати свою базу даних, щоб у вас було 3 різних таблиці для кожного типу профілю компанії. Використовуючи свій приклад, ви мали б таблиці з стовпцями:

USER_ID, LIKE_MOVIE, LIKE_MUSIC

USER_ID, FAVORITE_CUISINE

USER_ID, OWN_DOG, DOG_COUNT

Такий підхід передбачає, що ви знатимете форму інформації, яку компанія хоче зберігати раніше, і що вона не буде часто змінюватися. Якщо форма даних невідома під час проектування, ймовірно, буде краще перейти з цим полем JSON або носією бази даних.


-1

З тієї чи іншої причини бази даних - це те поле, в якому найчастіше виявляється ефект внутрішньої платформи. Це лише черговий випадок спливу анти-візерунка.

У цьому випадку ви намагаєтеся боротися з природним і правильним рішенням. Користувачі компанії A не є користувачами компанії B, і вони повинні мати власні таблиці для власних полів.

Ваш постачальник баз даних не стягує плату за таблицю, і вам не потрібно двічі дискового простору для двох таблиць (адже наявність двох таблиць є більш ефективною, оскільки ви не зберігаєте атрибути А. для користувачів групи B. Навіть зберігаєте лише NULLs займає простір).

Звичайно, якщо є достатньо загальних полів, ви можете розділити їх на загальну таблицю користувачів і мати зовнішній ключ у кожній із таблиць користувачів конкретної компанії. Це настільки проста структура, що жоден оптимізатор запитів бази даних не бореться з нею. Будь-яке необхідне ПРИЄДНУЙТЕ є тривіальним.


3
І якщо у вас є тисячі клієнтів, таблиця на кожного може швидко стати нездійсненною, не кажучи вже про те, що вам потрібен спеціальний код для спеціальних полів кожного клієнта.
Енді

@Andy: Вгадай, що? Ситуація буде ще незрозумілішою, якщо ви змішаєте тисячу різних схем в одну таблицю! І так, напевно, вам потрібен спеціальний код для користувацьких полів. Знову ж це простіше, а не складніше, якщо кожен клієнт має чистий окремий стіл. Спроба відібрати поля компанії X із тисячі інших - кривавий безлад.
MSalters

Ви посилаєтесь на мою відповідь чи ідею ОП про те, щоб позначити всі додаткові стовпці на таблиці клієнтів?
Енді

2
Мета тут - знайти ретельне та масштабоване рішення. Створення таблиці для кожного клієнта, безумовно, протилежне цьому. Кожного разу, коли ви перебуваєте на борту нового клієнта, це нереально: запустити сценарій створення таблиці, оновити код (об'єкти Entity) та повторно розгорнути.
tsOverflow

Вся ця ідея використання спільних таблиць для всіх клієнтів сама по собі є окремою дискусією архітектури SaaS, і є кілька вагомих причин тримати клієнтів у різних таблицях (або навіть у різних базах даних, що дозволяє створювати резервне копіювання / відновлення та масштабування кожного клієнта). У цьому сценарії створення стовпчиків кузотми в головній таблиці не є мозком. Я виступив із пропозицією, і мені цікаво, чому люди підтримують це лише тому, що їм не подобається такий підхід. Ефект внутрішньої платформи - це реальність: використовуючи модель EVA, ваш запит буде складнішим, економлячи складніше,
посилюючи

-1

Моє рішення передбачає, що ви б викликали цей запит з програми, і ви повинні мати можливість виконувати обробку після публікації. Ви можете мати такі стовпці:

ID, COMPANY_ID, FIRST_NAME, LAST_NAME, EMAIL, CUSTOM_VALUES

CUSTOM_VALUES буде тип рядка, що зберігає пара ключів і значень. ключ буде ім'ям стовпця, а значення - значенням стовпця, наприклад

LIKE_MOVIE;yes;LIKE_MUSIC;no;FAV_CUISINE;rice

у цьому CUSTOM_VALUES ви збережете лише ту інформацію, яка існує. Коли ви отримуєте запит від програми, ви можете розділити цей рядок і використовувати його.

Я використовував цю логіку, і вона чудово працює, її просто потрібно буде застосувати логіку фільтрування в коді, а не в запиті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.