Как создать базу данных для пользовательских полей? - PullRequest
135 голосов
/ 24 февраля 2011

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

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

Другая информация:

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

Параметры:

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

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

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

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

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

  6. Что-то еще?

Ответы [ 14 ]

46 голосов
/ 01 марта 2011

Если производительность является основной проблемой, я бы пошел с # 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. Дизайн логической таблицы может привести вас к определенной точке, нокогда количество записей становится действительно огромным, вы также должны начать смотреть на то, какие опции разбиения таблиц предоставляет выбранная вами СУБД.

21 голосов
/ 03 марта 2011

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

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

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

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

9 голосов
/ 01 марта 2011

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

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

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

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

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

Таблица UdfMetaData

  • int id
  • varchar Имя
  • тип varchar

Таблица MasterUdfValues ​​

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

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

8 голосов
/ 06 марта 2011

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

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

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

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

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

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

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

6 голосов
/ 05 марта 2011

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

Опция 1

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

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

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

Вариант 2,6

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

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

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

Разреженные столбцы

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

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

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

varchar Name
varchar Type
decimal NumberValue
varchar StringValue
date DateValue
4 голосов
/ 24 февраля 2011

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

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

EDIT

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

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

Postgress

MySQL

2 голосов
/ 24 февраля 2011

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

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

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

2 голосов
/ 24 февраля 2011

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

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

1 голос
/ 04 марта 2011

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

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

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

...