Как создать базу данных для пользовательских полей?

145

Мои требования:

  • Необходимо иметь возможность динамически добавлять определяемые пользователем поля любого типа данных
  • Необходимо уметь быстро запрашивать UDF
  • Нужно уметь делать вычисления на UDF на основе типа данных
  • Необходимо уметь сортировать пользовательские функции по типу данных

Дополнительная информация:

  • Я ищу производительность в первую очередь
  • Существует несколько миллионов основных записей, к которым можно прикрепить данные UDF
  • Когда я последний раз проверял, в нашей текущей базе данных было более 50 миллионов записей UDF
  • В большинстве случаев UDF прикрепляется только к нескольким тысячам основных записей, но не ко всем
  • UDF не объединяются и не используются в качестве ключей. Это просто данные, используемые для запросов или отчетов

Параметры:

  1. Создать большую таблицу с помощью StringValue1, StringValue2 ... IntValue1, IntValue2, ... и т. Д. Я ненавижу эту идею, но рассмотрю ее, если кто-то скажет мне, что она лучше других и почему.

  2. Создайте динамическую таблицу, которая добавляет новый столбец по требованию. Мне также не нравится эта идея, поскольку я чувствую, что производительность будет низкой, если вы не проиндексировали каждый столбец.

  3. Создайте одну таблицу, содержащую UDFName, UDFDataType и Value. Когда добавляется новый UDF, генерируется View, который извлекает только эти данные и анализирует их в соответствии с указанным типом. Элементы, которые не соответствуют критериям анализа, возвращают NULL.

  4. Создайте несколько таблиц UDF, по одной на тип данных. Таким образом, у нас будут таблицы для UDFStrings, UDFDates и т. Д. Вероятно, мы сделаем то же самое, что и # 2, и автоматически создадим View при каждом добавлении нового поля

  5. XML DataTypes? Я не работал с ними раньше, но видел, как они упоминались. Не уверен, что они дадут мне результаты, которые я хочу, особенно с производительностью.

  6. Что-то другое?

Рейчел
источник
7
Мартин Фаулер рекомендует 2 (обновляемая пользователем схема) или 5 (индексированный большой
Нил МакГиган,
См. Также вопрос StackOverflow о динамических схемах базы данных .
FloverOwe

Ответы:

49

Если производительность является основной задачей, я бы пошел с # 6 ... таблица на UDF (на самом деле, это вариант # 2). Этот ответ специально предназначен для этой ситуации, и описаны описания распределения данных и схемы доступа.

Плюсы:

  1. Поскольку вы указываете, что некоторые пользовательские функции имеют значения для небольшой части общего набора данных, отдельная таблица обеспечит вам наилучшую производительность, поскольку эта таблица будет настолько большой, насколько это необходимо для поддержки UDF. То же самое относится и к связанным индексам.

  2. Вы также получаете повышение скорости, ограничивая объем данных, которые должны обрабатываться для агрегации или других преобразований. Разделение данных на несколько таблиц позволяет выполнить некоторые агрегирующие и другие статистические анализы данных UDF, а затем соединить этот результат с мастер-таблицей через внешний ключ, чтобы получить неагрегированные атрибуты.

  3. Вы можете использовать имена таблиц / столбцов, которые отражают данные на самом деле.

  4. У вас есть полный контроль над использованием типов данных, проверкой ограничений, значений по умолчанию и т. Д. Для определения доменов данных. Не стоит недооценивать снижение производительности в результате преобразования типов данных «на лету». Такие ограничения также помогают оптимизаторам запросов СУБД разрабатывать более эффективные планы.

  5. Если вам когда-либо понадобится использовать внешние ключи, встроенная декларативная ссылочная целостность редко оказывается неэффективной из-за применения ограничений на уровне триггера или приложения.

Минусы:

  1. Это может создать много таблиц. Осуществление разделения схемы и / или соглашения об именовании облегчит это.

  2. Для работы с определением и управлением UDF требуется больше кода приложения. Я ожидаю, что это все еще меньше кода, необходимого, чем для исходных вариантов 1, 3 и 4.

Другие соображения:

  1. Если в природе данных есть что-то, что имело бы смысл сгруппировать UDF, это следует поощрять. Таким образом, эти элементы данных могут быть объединены в одну таблицу. Например, допустим, у вас есть UDF для цвета, размера и стоимости. Тенденция в данных такова, что большинство экземпляров этих данных выглядит

     'red', 'large', 45.03 

    скорее, чем

     NULL, 'medium', NULL

    В таком случае вы не понесете заметного снижения скорости, объединив 3 столбца в 1 таблице, потому что немногие значения будут равны NULL, и вы избежите создания еще 2 таблиц, а это на 2 объединения меньше, если вам нужен доступ ко всем 3 столбцам ,

  2. Если вы попали в стену производительности из UDF, который густонаселен и часто используется, то это следует учитывать для включения в основную таблицу.

  3. Конструкция логической таблицы может привести вас к определенной точке, но когда количество записей становится действительно огромным, вы также должны начать смотреть на то, какие опции разбиения таблиц предоставляют выбранная вами СУБД.

Фил Хелмер
источник
1
Перечни! Внутри шутка между мной и Филом, надеюсь, это не противоречит правилам.
GunnerL3510
Спасибо, я думаю, я сделаю несколько вариантов этого. Большая часть наших данных UDF поступает из несопоставленных полей импорта, которые нужно хранить только для справочных целей, поэтому я хотел бы поместить их в одну таблицу. Другие UDF определяются по мере необходимости (я не могу определить их заранее. Они обычно создаются, когда мы меняем какой-то процесс или решаем отслеживать что-то особенное в течение нескольких месяцев) и обычно используются в запросах. Я думаю, что я сделаю отдельную таблицу для каждой логической единицы этих значений.
Рэйчел
Я работаю с таблицей с датами / версиями UDF, я использую этот метод stackoverflow.com/a/123481/328968 , чтобы получить последние значения.
Питер
22

Я написал об этой проблеме много . Наиболее распространенным решением является antipattern Entity-Attribute-Value, который похож на то, что вы описали в своем варианте №3. Избегайте этого дизайна, как чума .

Когда я нуждаюсь в действительно динамических настраиваемых полях, я использую это решение для хранения их в виде большого двоичного объекта XML, чтобы я мог добавлять новые поля в любое время. Но, чтобы сделать это быстрее, также создайте дополнительные таблицы для каждого поля, в котором вам нужно искать или сортировать (у вас нет таблицы по полю - только таблица по запросу поле ). Это иногда называют инвертированным индексом.

Вы можете прочитать интересную статью об этом решении от 2009 года здесь: http://backchannel.org/blog/friendfeed-schemaless-mysql

Или вы можете использовать документно-ориентированную базу данных, где ожидается, что у вас есть настраиваемые поля для каждого документа. Я бы выбрал Solr .

Билл Карвин
источник
1
Можете ли вы объяснить, почему я должен избегать варианта № 3? Я посмотрел на некоторые из ваших примеров, но на самом деле они не совпадают с тем, что я пытаюсь сделать. Я просто хочу место для хранения дополнительных данных, а не место для хранения всех атрибутов.
Рэйчел
2
Для начала, кому бы вы присвоили атрибут NOT NULL? Как бы вы сделали атрибут UNIQUE, не сделав все атрибуты UNIQUE? Это продолжается оттуда. Вы заканчиваете тем, что пишете код приложения, чтобы обеспечить функции, которые уже предоставляет вам СУБД, даже до такой степени, что вам приходится писать какой-то класс отображения для простой вставки записи логического объекта и извлечения ее обратно.
Билл Карвин
2
Краткий ответ: «Не смешивайте данные и метаданные». Создание столбцов varchar для fieldnameили tablenameхранение идентификаторов метаданных в виде строк данных, и это начало многих проблем. Также см. En.wikipedia.org/wiki/Inner-platform_effect
Билл Карвин
2
@Thomas: в дизайне с инвертированным индексом вы можете использовать стандартные схемы решений для типов данных и ограничения, такие как UNIQUE и FOREIGN KEY. Они не работают вообще, когда вы используете EAV. Я согласен, что инвертированный индекс разделяет с EAV черту нереляции просто потому, что он поддерживает разные атрибуты в строке, но это точка компромисса.
Билл Карвин
2
@thitami, я узнал за эти годы, что любое решение может быть правильным для вашего приложения. Даже EAV может быть наименее плохим решением для какого-то конкретного приложения. Вы не можете выбрать стратегию оптимизации, не зная ваших запросов. Каждый вид оптимизации улучшает определенные запросы за счет других запросов.
Билл Карвин
10

Скорее всего, я бы создал таблицу следующей структуры:

  • имя varchar
  • Тип Varchar
  • десятичное число
  • varchar StringValue
  • date DateValue

Точные типы, конечно, зависят от ваших потребностей (и, конечно, от используемых вами БДМ). Вы также можете использовать поле NumberValue (десятичное) для целых и логических значений. Вам могут понадобиться и другие типы.

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

Вы можете хотеть иметь некоторую информацию метаданных. Таким образом, вы получите следующее:

Таблица UdfMetaData

  • int id
  • имя varchar
  • Тип Varchar

Таблица MasterUdfValues

  • int Master_FK
  • int MetaData_FK
  • десятичное число
  • varchar StringValue
  • date DateValue

Что бы вы ни делали, я бы не стал динамически менять структуру таблицы. Это кошмар обслуживания. Я бы также не использовал XML-структуры, они слишком медленные.

Стефан Штайнеггер
источник
Мне нравится ваша стратегия, и, возможно, я выберу ее, но в 2017 году вы выберете что-то другое? как json
maztt
В нашем проекте мы реализовали наши собственные структуры данных, которые сериализуются в нечто похожее на json. Он имеет интерфейс сохранения типов для чтения и записи данных без приведения и с отличной интеграцией с языком программирования. Это действительно здорово. У него та же проблема, что и у всех подобных «документов» в базах данных. Трудно запросить специальные значения, и он не может легко ссылаться на данные вне «документа». В зависимости от использования, оба не проблема.
Стефан
Кроме того, то, что я предложил в 2011 году, по-моему, остается верным решением.
Стефан
10

Это звучит как проблема, которая может быть лучше решена с помощью нереляционного решения, такого как MongoDB или CouchDB.

Они оба позволяют динамическое расширение схемы, позволяя вам поддерживать целостность кортежа, которую вы ищете.

Я согласен с Биллом Карвином, модель EAV не подходит для вас. Использование пар «имя-значение» в реляционной системе само по себе не плохо, а работает хорошо только тогда, когда пара «имя-значение» создает полный набор информации. При его использовании вы вынуждены динамически восстанавливать таблицу во время выполнения, все виды вещей начинают становиться все труднее. Запросы становятся упражнением в обслуживании сводной системы или вынуждают вас перенести реконструкцию кортежа на уровень объекта.

Вы не можете определить, является ли нулевое или отсутствующее значение допустимой записью или ее отсутствием, не внедрив правила схемы в слой объекта.

Вы теряете способность эффективно управлять своей схемой. Является ли 100-символьный varchar правильным типом для поля «значение»? 200-символов? Должно ли это быть nvarchar вместо этого? Это может быть сложный компромисс, который заканчивается тем, что вам приходится накладывать искусственные ограничения на динамическую природу вашего сета. Что-то вроде «вы можете иметь только x пользовательских полей, каждое из которых может быть длиной всего y символов.

С документно-ориентированным решением, таким как MongoDB или CouchDB, вы поддерживаете все атрибуты, связанные с пользователем, в одном кортеже. Поскольку объединения не являются проблемой, жизнь счастлива, поскольку ни один из этих двух не преуспевает с объединениями, несмотря на ажиотаж. Ваши пользователи могут определять столько атрибутов, сколько они хотят (или вы позволите) на длинах, которыми не сложно управлять, пока вы не достигнете около 4 МБ.

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

Data Monk
источник
6

Даже если вы предоставляете пользователю добавление пользовательских столбцов, это не обязательно означает, что запросы к этим столбцам будут работать хорошо. Есть много аспектов, которые входят в дизайн запросов, которые позволяют им работать хорошо, наиболее важным из которых является правильная спецификация того, что должно храниться в первую очередь. Таким образом, по сути, хотите ли вы позволить пользователям создавать схемы, не задумываясь о спецификациях, и иметь возможность быстро получать информацию из этой схемы? Если это так, то вряд ли любое такое решение будет хорошо масштабироваться, особенно если вы хотите позволить пользователю выполнять численный анализ данных.

Опция 1

IMO, этот подход дает вам схему без знания того, что означает схема, которая является рецептом для катастрофы и кошмаром для разработчиков отчетов. То есть вы должны иметь метаданные, чтобы знать, в каком столбце хранятся какие данные. Если эти метаданные будут испорчены, у них есть возможность связать ваши данные. Кроме того, это позволяет легко поместить неправильные данные в неправильный столбец. («Что? String1 содержит название монастыря? Я думал, что это были любимые наркотики Чали Шин».)

Вариант 3,4,5

ИМО, требования 2, 3 и 4 исключают любые вариации EAV. Если вам нужно запросить, отсортировать или выполнить вычисления на основе этих данных, EAV - это мечта Ктулху и кошмар вашей команды разработчиков и администратора баз данных. EAV создаст узкое место с точки зрения производительности и не обеспечит целостность данных, необходимую для быстрого доступа к необходимой информации. Запросы быстро превратятся в кросс-таблицу гордиевых узлов.

Вариант 2,6

Это действительно оставляет один выбор: собрать спецификации, а затем построить схему.

Если клиенту нужна максимальная производительность для данных, которые он хочет сохранить, ему необходимо пройти через процесс работы с разработчиком, чтобы понять его потребности, чтобы они сохранялись максимально эффективно. Он по-прежнему может храниться в таблице отдельно от остальных таблиц с кодом, который динамически создает форму на основе схемы таблицы. Если у вас есть база данных, которая допускает расширенные свойства для столбцов, вы даже можете использовать их, чтобы помочь построителю форм использовать красивые метки, всплывающие подсказки и т. Д., Чтобы все, что было необходимо, - это добавить схему. В любом случае, для эффективного создания и запуска отчетов данные должны храниться надлежащим образом. Если в рассматриваемых данных будет много нулей, в некоторых базах данных будет возможность хранить информацию такого типа. Например,

Если бы это был только пакет данных, для которого не нужно было выполнять анализ, фильтрацию или сортировку, я бы сказал, что некоторые вариации EAV могут помочь. Однако, учитывая ваши требования, наиболее эффективным решением будет получение правильных спецификаций, даже если вы храните эти новые столбцы в отдельных таблицах и динамически формируете формы из этих таблиц.

Редкие колонны

Томас
источник
5
  1. Создайте несколько таблиц UDF, по одной на тип данных. Таким образом, у нас будут таблицы для UDFStrings, UDFDates и т. Д. Вероятно, мы сделаем то же самое, что и # 2, и автоматически создадим View при каждом добавлении нового поля

Согласно моим исследованиям, несколько таблиц на основе типа данных не помогут вам в производительности. Особенно, если у вас есть объемные данные, такие как записи 20K или 25K с 50+ UDF. Производительность была худшей.

Вы должны пойти с одной таблицей с несколькими столбцами, такими как:

varchar Name
varchar Type
decimal NumberValue
varchar StringValue
date DateValue
Амит Подрядчик
источник
Это должно быть правильным и голосуемым. Предыдущий ответ Фила на 2011 год больше не является хорошим советом сегодня 2016.
Яп Кай Лун Леон
Могу ли я получить простой пример, как сделать такой процесс в SQL?
Нирой
Извините за поздний ответ, но вы хотите структуру базы данных для того же. Я не понял тебя @Niroj. Можете ли вы объяснить подробно, как вы хотите.
Подрядчик Амит
4

Это проблемная ситуация, и ни одно из решений не кажется «правильным». Однако вариант 1, вероятно, является лучшим как с точки зрения простоты, так и с точки зрения производительности.

Это также решение, используемое в некоторых коммерческих приложениях предприятия.

РЕДАКТИРОВАТЬ

другой вариант, который доступен сейчас, но не существовал (или, по крайней мере, не вступил в силу), когда был задан оригинальный вопрос, - это использовать поля json в БД.

многие реляционные БД теперь поддерживают поля на основе json (которые могут включать в себя динамический список подполей) и позволяют запрашивать их

Postgress

MySQL

Офир Йоктан
источник
1
Я ненавижу идею создания возможно сотен неиспользуемых столбцов. Это противоречит тому, что я узнал и прочитал о разработке баз данных SQL. В настоящее время у нас есть более 1300 различных пользовательских значений, хотя многие из них являются просто дубликатами существующих элементов, которые имеют разные названия.
Рэйчел
1300 разных UDF для одного стола? у каждого пользователя есть возможность добавить UDF, или только какой-то опытный пользователь?
Офир Йоктан
Это часть процесса импорта ... он добавляет любые несопоставленные данные в пользовательское поле. Поскольку никто не тратит время на сопоставление несопоставленных данных с существующими полями UDF, он просто создает новые, и за эти годы было добавлено много.
Рэйчел
2

У меня был опыт или 1, 3 и 4, и все они заканчиваются беспорядочно, поскольку неясно, что это за данные, или действительно сложно с какой-то мягкой категоризацией, чтобы разбить данные на динамические типы записей.

Я хотел бы попробовать XML, у вас должна быть возможность применять схемы к содержимому XML для проверки набора данных и т. Д., Что поможет хранить различные наборы данных UDF. В новых версиях SQL-сервера вы можете индексировать поля XML, что должно способствовать повышению производительности. (см., например, http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx )

Джон Эгертон
источник
Честно говоря, я вообще не изучал XML. Основным недостатком этого является то, что мне придется узнать, как это работает и как выполнять запросы к нему, и я слышал, что производительность может быть хуже, чем другие варианты
Рэйчел
1
Я бы не использовал для этого xml: он может выполнять свою работу, и я реализовал нечто подобное в xml в прошлом, но производительность сильно ухудшилась по мере роста структур данных и высокой сложности кода.
Келл
2

Если вы используете SQL Server, не пропустите тип sqlvariant. Это довольно быстро и должно делать вашу работу. Другие базы данных могут иметь что-то подобное.

Типы данных XML не так хороши с точки зрения производительности. Если вы выполняете вычисления на сервере, вам постоянно приходится десериализовать их.

Вариант 1 звучит плохо и выглядит грубовато, но с точки зрения производительности это может быть вашим лучшим выбором. Я создал таблицы со столбцами с именем Field00-Field99 и раньше, потому что вы просто не можете превзойти производительность. Возможно, вам также придется учитывать производительность INSERT, и в этом случае это тоже самое. Вы всегда можете создать представления для этой таблицы, если хотите, чтобы она выглядела аккуратно!

Тим Роджерс
источник
Спасибо, я еще раз посмотрю на варианты SQL. Больше всего меня беспокоит производительность, и я не уверен, как она справится с этим, особенно если мы говорим о 50-миллионных рядах
Рэйчел
Только что обнаруженные переменные sql_varients нельзя использовать с предложением LIKE ... это огромный недостаток для меня. Конечно, если я создаю представление для каждого UDF, я мог бы привести его к соответствующему типу данных, основанному на SQL_VARIANT_PROPERTY (value, 'BaseType') ... ... тем не менее, это кажется плохим для производительности
Rachel
Вы можете использовать LIKE, но сначала вам нужно привести значение. LIKE работает только с varchars, поэтому вы должны привести свой sql_variant к varchar. Пока вы знаете, что ваш UDF является varchar (например, потому что тип хранится где-то еще), вы можете отфильтровать все ваши строки в varchars, затем привести и выполнить ваш запрос LIKE: например. выберите * FROM MyTable, где variable_type = 'v' Cast (variable_value as varchar (max)) LIKE 'Blah%' Таким образом, вы не конвертируете int и т. д. в строки, которые замедляют работу.
Тим Роджерс
Мне нужно выполнить несколько тестов, чтобы увидеть, как это влияет на производительность, особенно с миллионами строк. Знаете какие-нибудь онлайн-статьи о производительности с использованием sql_varients? Особенно с кастингом и очень большим количеством записей?
Рэйчел
1

SharePoint использует вариант 1 и имеет разумную производительность.

Натан Девитт
источник
1

Я успешно справлялся с этим в прошлом, не используя ни один из этих вариантов (вариант 6? :)).

Я создаю модель для пользователей, с которыми можно поиграть (сохранить в формате xml и представить с помощью пользовательского инструмента моделирования) и из таблиц и представлений, сгенерированных моделью, чтобы объединить базовые таблицы с пользовательскими таблицами данных. Таким образом, каждый тип будет иметь базовую таблицу с основными данными и пользовательскую таблицу с определенными пользователем полями.

Возьмите документ в качестве примера: типичные поля: имя, тип, дата, автор и т. Д. Это будет указано в основной таблице. Затем пользователи будут определять свои собственные специальные типы документов со своими собственными полями, такими как contract_end_date, renewal_clause, бла-бла-бла. Для этого определенного пользователем документа будет таблица базового документа, таблица xcontract, объединенная по общему первичному ключу (поэтому первичный ключ xcontracts также является внешним по отношению к первичному ключу базовой таблицы). Затем я бы сгенерировал представление, чтобы обернуть эти две таблицы. Производительность при запросах была быстрой. дополнительные бизнес-правила также могут быть встроены в представления. Это сработало очень хорошо для меня.

Келл
источник
1

Наша база данных поддерживает SaaS-приложение (программное обеспечение службы поддержки), где пользователи имеют более 7 тыс. «Пользовательских полей». Мы используем комбинированный подход:

  1. (EntityID, FieldID, Value)таблица для поиска данных
  2. поле JSON в entitiesтаблице, в котором хранятся все значения сущностей, используемые для отображения данных. (таким образом, вам не нужен миллион JOIN для получения значений значений).

Вы могли бы далее разделить # 1, чтобы иметь «таблицу на тип данных», как предлагает этот ответ , таким образом, вы даже можете индексировать свои UDF.

PS Пару слов в защиту подхода «Entity-Attribute-Value», который все продолжают ругать. Мы использовали # 1 без # 2 в течение десятилетий, и это работало просто отлично. Иногда это деловое решение. У вас есть время, чтобы переписать ваше приложение и перепроектировать БД, или вы можете бросить пару долларов на облачные серверы, которые действительно дешевы в наши дни? Кстати, когда мы использовали подход № 1, наша БД содержала миллионы сущностей, к которым обращались 100 тысяч пользователей, а двухъядерный 16-Гбайт сервер на 16 Гб работал нормально.

Alex
источник
Привет @ Алекс, я столкнулся с подобной проблемой. Если я хорошо понимаю, у вас есть: 1) custom_fieldsтаблица, в которой хранятся значения, такие как 1 => last_concert_year, 2 => band, 3 =>, musicа затем custom_fields_valuesтаблица со значениями 001, 1, 1976 002, 1, 1977 003, 2, Iron Maiden003, 3 , Metal Надежда пример имеет смысл к вам и извиняюсь за форматирование!
thitami
@ Это не совсем так. По вашему примеру: у меня есть bandsтаблица со строкой, 1,'Iron Maiden'затем custom_fieldsсо строками, а 1,'concert_year' | 2,'music'затем custom_fields_valuesсо строками1,1,'1977'|1,2,'metal'
Алекс
0

В комментариях я видел, что вы говорите, что поля UDF должны выводить импортированные данные, которые не отображаются должным образом пользователем.

Возможно, другой вариант - отследить количество пользовательских функций, созданных каждым пользователем, и заставить их повторно использовать поля, сказав, что они могут использовать 6 (или некоторые другие одинаково произвольно ограниченные) верхние значения пользовательских полей.

Когда вы сталкиваетесь с такой проблемой структурирования базы данных, часто лучше вернуться к базовому дизайну приложения (в вашем случае - системе импорта) и наложить на него еще несколько ограничений.

Теперь, что бы я сделал, это вариант 4 (РЕДАКТИРОВАТЬ) с добавлением ссылки на пользователей:

general_data_table
id
...


udfs_linked_table
id
general_data_id
udf_id


udfs_table
id
name
type
owner_id --> Use this to filter for the current user and limit their UDFs
string_link_id --> link table for string fields
int_link_id
type_link_id

Теперь убедитесь, что вы создали представления, чтобы оптимизировать производительность и правильно настроить индексы. Этот уровень нормализации уменьшает размер базы данных, но делает ваше приложение более сложным.

Воутер Саймонс
источник
0

Я бы порекомендовал # 4, поскольку этот тип системы использовался в Magento, которая является высоко аккредитованной платформой CMS для электронной коммерции. Используйте одну таблицу для определения ваших пользовательских полей, используя fieldId и метки столбцов. Затем имейте отдельные таблицы для каждого типа данных, и в каждой из этих таблиц есть индекс, который индексирует по fieldId и столбцам значений типа данных . Затем в своих запросах используйте что-то вроде:

SELECT *
FROM FieldValues_Text
WHERE fieldId IN (
    SELECT fieldId FROM Fields WHERE userId=@userId
)
AND value LIKE '%' + @search + '%'

На мой взгляд, это обеспечит наилучшую производительность для пользовательских типов.

Исходя из своего опыта, я работал на нескольких веб-сайтах Magento, которые обслуживают миллионы пользователей в месяц, размещают тысячи продуктов с настраиваемыми атрибутами продуктов, а база данных легко справляется с нагрузкой, даже для отчетов.

Для создания отчетов вы можете использовать их PIVOTдля преобразования значений меток таблицы Fields в имена столбцов, а затем сводить результаты запроса из каждой таблицы типов данных в эти столбцы.

Марк Энтинг
источник