Каковы шаблоны проектирования для поддержки настраиваемых полей в приложении?
Мы разрабатываем коммерческое приложение. Наши клиенты просят поддержку пользовательских полей. Например, они хотят добавить поле в форму клиента.
Каковы известные шаблоны проектирования для хранения значений полей и метаданных о полях?
Я вижу следующие параметры:
Вариант 1: добавьте поля Field1, Field2, Field3, Field4 типа varchar в таблицу Customer.
Вариант 2. Добавьте один столбец типа XML в таблицу клиентов и сохраните значения настраиваемых полей в xml.
Вариант 3: добавьте таблицу CustomerCustomFieldValue со столбцом типа varchar и сохраните значения в этом столбце. Эта таблица также будет иметь идентификатор CustomerID, CustomFieldID.
CustomerID, CustomFieldID, Value
10001, 1001, '02/12/2009 8:00 AM'
10001, 1002, '18.26'
10002, 1001, '01/12/2009 8:00 AM'
10002, 1002, '50.26'
CustomFieldID будет идентификатором из другой таблицы под названием CustomField с этими столбцами: CustomFieldID, FieldName, FieldValueTypeID.
Вариант 4: добавьте таблицу CustomerCustomFieldValue со столбцом каждого возможного типа значений и сохраните значения в правом столбце. Аналогично # 3, но значения полей хранятся с использованием столбца строгого типа.
CustomerID, CustomFieldID, DateValue, StringValue, NumericValue
10001, 1001, 02/12/2009 8:00 AM, null, null
10001, 1002, null, null, 18.26
10002, 1001, 01/12/2009 8:00 AM, null, null
10002, 1002, null, null, 50.26
Вариант 5. Варианты 3 и 4 используют таблицу, относящуюся к одной концепции (Клиент). Наши клиенты также запрашивают пользовательские поля в других формах. Должна ли мы вместо этого иметь общесистемную систему хранения пользовательских полей? Поэтому вместо того, чтобы иметь несколько таблиц, таких как CustomerCustomFieldValue, EmployeeCustomFieldValue, InvoiceCustomFieldValue, у нас была бы одна таблица с именем CustomFieldValue? Хотя для меня это кажется более изящным, не может ли это вызвать узкое место в производительности?
Вы использовали какой-либо из этих подходов? Вы были успешны? Какой подход вы бы выбрали?
Знаете ли вы какой-либо другой подход, который я должен рассмотреть?
Кроме того, мои клиенты хотят, чтобы пользовательское поле могло ссылаться на данные в других таблицах. Например, клиент может захотеть добавить к "Клиенту" поле "Любимый способ оплаты". Способы оплаты определяются в других местах системы. Это приводит к теме "внешних ключей" на картинке. Должен ли я попытаться создать ограничения, чтобы гарантировать, что значения, хранящиеся в пользовательских таблицах полей, являются допустимыми значениями?
Спасибо
======================
EDIT 07-27-2009:
Спасибо за ваши ответы. Похоже, что список подходов теперь довольно всеобъемлющий. Я выбрал вариант 2 (один столбец XML). На данный момент это было проще всего реализовать. Мне, вероятно, придется преувеличивать более строго определенный подход, так как мои требования будут более сложными, а количество поддерживаемых настраиваемых полей будет больше.
Ответы
Ответ 1
Я согласен с плакатами ниже, что варианты 3, 4 или 5, скорее всего, будут уместны. Однако каждая из предлагаемых вами реализаций имеет свои преимущества и затраты. Я бы предложил выбрать один, сопоставив его с вашими конкретными требованиями. Например:
- Вариант 1: Быстрый для реализации. Позволяет выполнять действия DB на настраиваемых полях (поиск, сортировка.)
Вариант 1 минус: пользовательские поля являются общими, поэтому нет строго типизированных полей. Таблица базы данных неэффективна, по размеру со многими посторонними полями, которые никогда не будут использоваться. Необходимо указать количество настраиваемых полей.
- Вариант 2: Быстро реализовать. Гибкость, позволяющая произвольное количество и тип настраиваемых полей.
Вариант 2 минус: Никакие действия DB не возможны в настраиваемых полях. Это лучше всего, если все, что вам нужно сделать, это отобразить пользовательские поля, позже или сделать небольшие манипуляции с данными только на основе каждого клиента.
- Преимущества 3-го варианта: гибкие и эффективные. Действия DB могут быть выполнены, но данные нормализуются несколько, чтобы уменьшить потраченное впустую пространство. Я согласен с неизвестным (google) предложением добавить дополнительный столбец, который можно использовать для указания типа или исходной информации.
Вариант 3 минус: Небольшое увеличение времени разработки и сложности ваших запросов, но здесь действительно не так много минусов.
- Вариант 4 совпадает с вариантом 3, за исключением того, что ваши типизированные данные могут работать на уровне БД. Добавление информации о типе в таблицу ссылок в Варианте 3 позволяет выполнять больше операций на уровне нашего приложения, но БД не сможет выполнять сравнения или сортировать, например. Выбор между 3 и 4 зависит от этого требования.
- Вариант 5 такой же, как 3 или 4, но с еще большей гибкостью для применения решения для многих разных таблиц. Стоимость в этом случае будет заключаться в том, что размер этой таблицы будет намного больше. Если вы выполняете много дорогостоящих операций соединения, чтобы перейти к своим настраиваемым полям, это решение может плохо масштабироваться.
P.S. Как отмечено ниже, термин "шаблон проектирования" обычно относится к объектно-ориентированному программированию. Вы ищете решение проблемы с дизайном базы данных, что означает, что большинство рекомендаций, касающихся шаблонов проектирования, не будут применяться.
Ответ 2
Что касается кода приложения, я не уверен. Я знаю, что пользовательские поля в значительной степени выигрывают от
Ответ 3
Если вы разрабатываете объектно-ориентированный язык, здесь мы говорим о адаптивных объектных моделях. Существует немало статей, написанных о том, как вы можете реализовать их в оо-языках, но не так много информации о том, как создать структуру хранилища данных.
В компании, где я работаю, мы решили проблему, используя реляционную базу данных для хранения данных AOM. У нас есть таблица с центральными сущностями для представления всех разных "сущностей" в домене, таких как люди, сетевые устройства, компании и т.д. Мы сохраняем фактические "поля формы" для таблиц данных, которые набираются, поэтому у нас есть одна таблица для строки, один для дат и так далее. Все таблицы данных имеют внешний ключ, указывающий на таблицу сущностей. Нам также нужны таблицы для представления типа стороны, то есть какие атрибуты (поля формы) могут иметь определенный объект и эта информация используется для интерпретации данных в таблицах данных.
Плюсы нашего решения заключаются в том, что все может быть смоделировано без изменений кода, включая ссылки между объектами, многозначными и так далее. Также возможно добавлять бизнес-правила и проверки в поля, и их можно использовать повторно во всех формах. Недостатки в том, что модель программирования не очень проста для понимания, а производительность запросов будет хуже, чем при более типичном дизайне БД. Некоторое другое решение, чем реляционная база данных, могло быть лучше и проще для AOM.
Построение хорошего AOM с рабочим хранилищем данных для него - большая работа, и я бы не рекомендовал его, если у вас нет высококвалифицированных разработчиков. Возможно, когда-нибудь будет решение ОС для этих требований.
Пользовательские поля обсуждались ранее в SO:
Ответ 4
Что-то вроде варианта 3 - это путь, и я использовал этот метод ранее. Создайте единую таблицу для определения дополнительных свойств и их соответствующих значений. Это будет 1-N взаимосвязь между вашим клиентом и таблицей CustomerCustomField (соответственно). Второй вопрос, связанный с определением отношений с пользовательскими свойствами, - это то, о чем нужно подумать. Первое, что приходит на ум - это добавить поле DataSource, которое будет содержать таблицу, к которой привязано значение свойства. Таким образом, ваш CustomerCustomField будет выглядеть так:
- CustomerId
- Свойство
- Значение
- ValueDataSource (с нулевым значением)
Это должно позволить вам либо привязываться к определенной структуре данных, либо просто указывать несвязанные значения. Вы можете нормализовать эту модель, но что-то вроде этого может работать и должно быть достаточно простым для обработки кода.
Ответ 5
Вариант 4 или 5 был бы моим выбором. Если ваши данные важны, я бы не стал отбрасывать информацию о вашем типе с помощью варианта 3. (Вы можете попытаться реализовать полную проверку типов самостоятельно, но это довольно большая работа, и механизм базы данных уже делает это для вас.)
Некоторые мысли:
- Убедитесь, что ваш
CustomFields
имеет столбец DataType
.
- Используйте ограничение проверки на основе UDF на
CustomFieldValues
, чтобы убедиться, что столбец, указанный CustomFields.DataType
, не равен нулю.
- Вам также потребуется стандартное ограничение проверки, чтобы убедиться, что у вас есть только одно ненулевое значение.
- Что касается внешних ключей, я бы смоделировал их как отдельный
DataType
.
- Каждая потенциальная ссылка для перекрестных таблиц потребует наличия собственного столбца. Это хорошо, потому что он поддерживает ссылочную целостность.
- В любом случае вам придется поддерживать эти отношения в коде приложения, поэтому факт, что они жестко закодированы в базе данных, фактически не ограничивает функциональность.
- Это также хорошо справится с вашим ORM, если вы его используете.
- Для варианта 5 используйте промежуточные таблицы для моделирования отношений.
- У вас все равно будет
CustomerCustomFieldValue
, но вместо этого только столбцы CustomerID
и CustomFieldValueID
.
- Подумайте, долго и упорно о ваших ограничений каждый шаг пути. Это сложный материал, и одна ошибка может привести к полному хауку по линии.
Я использую это в приложении, которое в настоящее время находится в разработке. Проблем еще не было, но дизайн EAV по-прежнему пугает меня. Просто будьте осторожны.
В стороне, XML также может быть хорошим выбором. Я не знаю столько об этом из непосредственного опыта, но это был один из вариантов, которые я рассматривал при запуске дизайна данных, и он выглядел довольно многообещающим.
Ответ 6
если эти дополнительные поля являются случайными и не хотят выполнять поиск на них, я обычно выбираю вариант 2 (но, как JSON, лучше, чем XML). Если в пользовательских полях будут выполняться поиски, опция 3 не сложна, и, как правило, оптимизатор SQL может получить разумную производительность.
Ответ 7
В настоящее время я работаю над проектом с этой же проблемой, и я решил использовать параметр 3, но я добавил поле FieldType и поле ListSource в случае FieldType = "list". Поле ListSource может быть запросом, представлением sql, именем функции или чем-то, что приводит к списку опций для списка. Самая большая проблема с попыткой хранения таких полей в моей ситуации заключается в том, что этот список полей может измениться, и пользователям разрешено редактировать данные позже. Итак, что делать, если список полей изменился, и они переходят к редактированию. Мое решение этого сценария состояло в том, чтобы разрешить редактирование только в том случае, если список не изменился и отобразить данные только для чтения, если он имеет.