Вопрос заключается в том, как мне проектировать базу данных, это могут быть реляционные базы данных / 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
Я посмотрел в поле PostgreSQL JSON, в этом случае у вас будет:
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", которая будет по существу хранить пользовательские атрибуты в строках, а не столбцах.
Есть 2 проблемы с этим подходом:
1) Данные растут в расчете на пользователя в виде строк, а не столбцов - и это означает, что для получения полной картины пользователя необходимо выполнить много соединений, несколько соединений с таблицей «пользовательский профиль» с различными пользовательскими атрибутами
2) Значение данных всегда сохраняется как VARCHAR, чтобы быть универсальным, даже если мы знаем, что данные должны быть целыми или логическими и т. Д.
источник
Ответы:
Пожалуйста, рассмотрите это как альтернативу. Оба предыдущих примера потребуют внесения изменений в схему по мере расширения области приложения, кроме того, решение «custom_column» сложно расширять и поддерживать. В конечном итоге вы получите Custom_510, а затем просто представьте, как ужасно будет работать с этой таблицей.
Сначала давайте воспользуемся схемой вашей компании.
Далее мы также будем использовать вашу схему Users для обязательных атрибутов верхнего уровня, которые будут использоваться / использоваться всеми компаниями.
Затем мы создадим таблицу, в которой мы определим наши динамические атрибуты, которые являются специфическими для пользовательских компаний. Итак, вот пример значения столбца Attribute будет «LikeMusic»:
Далее мы определяем таблицу UserAttributes, которая будет содержать значения атрибутов пользователя
Это может быть изменено многими способами, чтобы улучшить производительность. Вы можете использовать несколько таблиц для UserAttributes, делая каждую из них специфичной для типа данных, хранящихся в Value, или просто оставить его как VarChar и работать с ним как с хранилищем значений ключей.
Вы также можете переместить CompanyId из таблицы UserAttributeDefiniton в таблицу перекрестных ссылок для проверки в будущем.
источник
Используйте базу данных NoSQL. Там будут документы компании и пользователя. У пользователей будет часть их схемы, динамически создаваемая на основе шаблона пользователя (текст, указывающий поля / типы для этой компании.
Вот как это может выглядеть в Firebase.com. Вам нужно будет научиться делать это на любом другом языке.
источник
Если вы часто сталкиваетесь с настраиваемыми полевыми запросами, я бы фактически смоделировал их аналогично базе данных. Создайте таблицу, содержащую метаданные о каждом настраиваемом поле, CompanyCustomField (кому оно принадлежит, тип данных и т. Д.) И другую таблицу CompanyCustomFieldValues, которая содержит CustomerId, FieldId и значение. Если вы используете что-то вроде Microsoft Sql Server, у меня будет столбец значения типа sql_variant.
Конечно, это нелегко, поскольку вам понадобится интерфейс, позволяющий администраторам определять настраиваемые поля для каждого клиента, и другой интерфейс, который фактически использует эти метаданные для создания пользовательского интерфейса для сбора значений полей. И если у вас есть другие требования, такие как группирование полей вместе или необходимость сделать поле списка выбора, вам нужно будет дополнить его дополнительными метаданными / другими таблицами (например, CompanyCustomFieldPickListOptions).
Это не тривиально, но имеет то преимущество, что не требует изменений базы данных / кода для каждого нового настраиваемого поля. Любые другие функции настраиваемых полей также должны быть закодированы (например, если вы хотите регулярное выражение проверять строковое значение или разрешать только даты между определенными диапазонами, или если вам нужно включить одно настраиваемое поле на основе другого значения настраиваемого поля) ).
источник
Альтернативой другим ответам является наличие таблицы с именем profile_attrib или аналогичной, чтобы схема полностью управлялась вашим приложением.
По мере добавления пользовательских атрибутов
ALTER TABLE profile_attrib ADD COLUMN like_movie TINYINT(1)
вы можете запретить их удаление. Это сведет к минимуму ваше присоединение, но при этом обеспечит гибкость.Я полагаю, что компромисс в том, что теперь приложению необходимо изменить привилегии таблиц для базы данных, и вы должны быть умны в отношении очистки имен столбцов.
источник
[^\w-]+
должно довольно хорошо это делать, не допуская ничего, что не0-9A-Za-z_-
является - но да, санитарная обработка необходима для защиты от злонамеренности или глупости.Ваш вопрос имеет много потенциальных решений. Одним из решений является сохранение дополнительных атрибутов в формате XML. XML может быть сохранен как текст или если вы используете базу данных, которая поддерживает типы XML как XML (SQL Server). Хранение в виде текста ограничивает ваши возможности запросов (например, поиск по пользовательскому атрибуту), но если хранение и поиск - все, что вам нужно, то это хорошее решение. Если нужно выполнить запрос, лучше сохранить XML как тип XML (хотя это зависит от поставщика).
Это даст возможность хранить любое количество атрибутов для клиента, просто добавив дополнительный столбец в таблицу клиентов. Можно хранить атрибуты в виде хэш-набора или словаря, можно потерять безопасность типов, поскольку все будет начинаться со строки, но если применить стандартную строку формата для дат, чисел, логических значений, все получится, ОК.
Чтобы получить больше информации:
https://msdn.microsoft.com/en-us/library/hh403385.aspx
Ответ @ WalterMitty также действителен, хотя, если у вас много клиентов с разными атрибутами, можно получить много таблиц, если следовать модели наследования. Это зависит от того, сколько пользовательских атрибутов совместно используется клиентами.
источник
Вам следует нормализовать базу данных таким образом, чтобы у вас было 3 разные таблицы для каждого типа профиля компании. Используя ваш пример, у вас будут таблицы со столбцами:
Этот подход предполагает, что вы будете знать форму информации, которую компания хочет хранить заранее, и что она не будет часто меняться. Если форма данных неизвестна во время разработки, вероятно, было бы лучше использовать это поле JSON или базу данных nosql.
источник
По той или иной причине базы данных - это та область, в которой эффект внутренней платформы проявляется чаще всего. Это просто еще один случай появления анти-паттерна.
В этом случае вы пытаетесь бороться с естественным и правильным решением. Пользователи компании A не являются пользователями компании B, и у них должны быть свои таблицы для своих полей.
Поставщик базы данных не взимает с вас плату за таблицу, и вам не нужно удваивать дисковое пространство для удвоения количества таблиц (фактически, наличие двух таблиц более эффективно, поскольку вы не сохраняете атрибуты A для пользователей B. Даже хранение только NULL занимает место).
Конечно, если есть достаточно общих полей, вы можете выделить их в общую таблицу Users и иметь внешний ключ в каждой из пользовательских таблиц компании. Это настолько простая структура, что с ней не может справиться ни один оптимизатор запросов к базе данных. Любое необходимое СОЕДИНЕНИЕ тривиально.
источник
Мое решение предполагает, что вы будете вызывать этот запрос из программы, и вы сможете выполнить постобработку. Вы можете иметь следующие столбцы:
CUSTOM_VALUES будет иметь тип строки хранения ключ и пара значений. ключ будет именем столбца, а значение будет значением столбца, например
в этом CUSTOM_VALUES вы будете сохранять только ту информацию, которая существует. Когда вы делаете запрос из программы, вы можете разбить эту строку и использовать ее.
Я использовал эту логику, и она отлично работает, просто вам придется применять логику фильтрации в коде, а не в запросе.
источник