Мои требования:
- Необходимо иметь возможность динамически добавлять определяемые пользователем поля любого типа данных
- Необходимо уметь быстро запрашивать UDF
- Нужно уметь делать вычисления на UDF на основе типа данных
- Необходимо уметь сортировать пользовательские функции по типу данных
Дополнительная информация:
- Я ищу производительность в первую очередь
- Существует несколько миллионов основных записей, к которым можно прикрепить данные UDF
- Когда я последний раз проверял, в нашей текущей базе данных было более 50 миллионов записей UDF
- В большинстве случаев UDF прикрепляется только к нескольким тысячам основных записей, но не ко всем
- UDF не объединяются и не используются в качестве ключей. Это просто данные, используемые для запросов или отчетов
Параметры:
Создать большую таблицу с помощью StringValue1, StringValue2 ... IntValue1, IntValue2, ... и т. Д. Я ненавижу эту идею, но рассмотрю ее, если кто-то скажет мне, что она лучше других и почему.
Создайте динамическую таблицу, которая добавляет новый столбец по требованию. Мне также не нравится эта идея, поскольку я чувствую, что производительность будет низкой, если вы не проиндексировали каждый столбец.
Создайте одну таблицу, содержащую UDFName, UDFDataType и Value. Когда добавляется новый UDF, генерируется View, который извлекает только эти данные и анализирует их в соответствии с указанным типом. Элементы, которые не соответствуют критериям анализа, возвращают NULL.
Создайте несколько таблиц UDF, по одной на тип данных. Таким образом, у нас будут таблицы для UDFStrings, UDFDates и т. Д. Вероятно, мы сделаем то же самое, что и # 2, и автоматически создадим View при каждом добавлении нового поля
XML DataTypes? Я не работал с ними раньше, но видел, как они упоминались. Не уверен, что они дадут мне результаты, которые я хочу, особенно с производительностью.
Что-то другое?
Ответы:
Если производительность является основной задачей, я бы пошел с # 6 ... таблица на UDF (на самом деле, это вариант # 2). Этот ответ специально предназначен для этой ситуации, и описаны описания распределения данных и схемы доступа.
Плюсы:
Поскольку вы указываете, что некоторые пользовательские функции имеют значения для небольшой части общего набора данных, отдельная таблица обеспечит вам наилучшую производительность, поскольку эта таблица будет настолько большой, насколько это необходимо для поддержки UDF. То же самое относится и к связанным индексам.
Вы также получаете повышение скорости, ограничивая объем данных, которые должны обрабатываться для агрегации или других преобразований. Разделение данных на несколько таблиц позволяет выполнить некоторые агрегирующие и другие статистические анализы данных UDF, а затем соединить этот результат с мастер-таблицей через внешний ключ, чтобы получить неагрегированные атрибуты.
Вы можете использовать имена таблиц / столбцов, которые отражают данные на самом деле.
У вас есть полный контроль над использованием типов данных, проверкой ограничений, значений по умолчанию и т. Д. Для определения доменов данных. Не стоит недооценивать снижение производительности в результате преобразования типов данных «на лету». Такие ограничения также помогают оптимизаторам запросов СУБД разрабатывать более эффективные планы.
Если вам когда-либо понадобится использовать внешние ключи, встроенная декларативная ссылочная целостность редко оказывается неэффективной из-за применения ограничений на уровне триггера или приложения.
Минусы:
Это может создать много таблиц. Осуществление разделения схемы и / или соглашения об именовании облегчит это.
Для работы с определением и управлением UDF требуется больше кода приложения. Я ожидаю, что это все еще меньше кода, необходимого, чем для исходных вариантов 1, 3 и 4.
Другие соображения:
Если в природе данных есть что-то, что имело бы смысл сгруппировать UDF, это следует поощрять. Таким образом, эти элементы данных могут быть объединены в одну таблицу. Например, допустим, у вас есть UDF для цвета, размера и стоимости. Тенденция в данных такова, что большинство экземпляров этих данных выглядит
скорее, чем
В таком случае вы не понесете заметного снижения скорости, объединив 3 столбца в 1 таблице, потому что немногие значения будут равны NULL, и вы избежите создания еще 2 таблиц, а это на 2 объединения меньше, если вам нужен доступ ко всем 3 столбцам ,
Если вы попали в стену производительности из UDF, который густонаселен и часто используется, то это следует учитывать для включения в основную таблицу.
Конструкция логической таблицы может привести вас к определенной точке, но когда количество записей становится действительно огромным, вы также должны начать смотреть на то, какие опции разбиения таблиц предоставляют выбранная вами СУБД.
источник
Я написал об этой проблеме много . Наиболее распространенным решением является antipattern Entity-Attribute-Value, который похож на то, что вы описали в своем варианте №3. Избегайте этого дизайна, как чума .
Когда я нуждаюсь в действительно динамических настраиваемых полях, я использую это решение для хранения их в виде большого двоичного объекта XML, чтобы я мог добавлять новые поля в любое время. Но, чтобы сделать это быстрее, также создайте дополнительные таблицы для каждого поля, в котором вам нужно искать или сортировать (у вас нет таблицы по полю - только таблица по запросу поле ). Это иногда называют инвертированным индексом.
Вы можете прочитать интересную статью об этом решении от 2009 года здесь: http://backchannel.org/blog/friendfeed-schemaless-mysql
Или вы можете использовать документно-ориентированную базу данных, где ожидается, что у вас есть настраиваемые поля для каждого документа. Я бы выбрал Solr .
источник
fieldname
илиtablename
хранение идентификаторов метаданных в виде строк данных, и это начало многих проблем. Также см. En.wikipedia.org/wiki/Inner-platform_effectСкорее всего, я бы создал таблицу следующей структуры:
Точные типы, конечно, зависят от ваших потребностей (и, конечно, от используемых вами БДМ). Вы также можете использовать поле NumberValue (десятичное) для целых и логических значений. Вам могут понадобиться и другие типы.
Вам нужна ссылка на основные записи, которым принадлежит значение. Вероятно, проще и быстрее создать таблицу пользовательских полей для каждой мастер-таблицы и добавить простой внешний ключ. Таким образом, вы можете легко и быстро фильтровать основные записи по пользовательским полям.
Вы можете хотеть иметь некоторую информацию метаданных. Таким образом, вы получите следующее:
Таблица UdfMetaData
Таблица MasterUdfValues
Что бы вы ни делали, я бы не стал динамически менять структуру таблицы. Это кошмар обслуживания. Я бы также не использовал XML-структуры, они слишком медленные.
источник
Это звучит как проблема, которая может быть лучше решена с помощью нереляционного решения, такого как MongoDB или CouchDB.
Они оба позволяют динамическое расширение схемы, позволяя вам поддерживать целостность кортежа, которую вы ищете.
Я согласен с Биллом Карвином, модель EAV не подходит для вас. Использование пар «имя-значение» в реляционной системе само по себе не плохо, а работает хорошо только тогда, когда пара «имя-значение» создает полный набор информации. При его использовании вы вынуждены динамически восстанавливать таблицу во время выполнения, все виды вещей начинают становиться все труднее. Запросы становятся упражнением в обслуживании сводной системы или вынуждают вас перенести реконструкцию кортежа на уровень объекта.
Вы не можете определить, является ли нулевое или отсутствующее значение допустимой записью или ее отсутствием, не внедрив правила схемы в слой объекта.
Вы теряете способность эффективно управлять своей схемой. Является ли 100-символьный varchar правильным типом для поля «значение»? 200-символов? Должно ли это быть nvarchar вместо этого? Это может быть сложный компромисс, который заканчивается тем, что вам приходится накладывать искусственные ограничения на динамическую природу вашего сета. Что-то вроде «вы можете иметь только x пользовательских полей, каждое из которых может быть длиной всего y символов.
С документно-ориентированным решением, таким как MongoDB или CouchDB, вы поддерживаете все атрибуты, связанные с пользователем, в одном кортеже. Поскольку объединения не являются проблемой, жизнь счастлива, поскольку ни один из этих двух не преуспевает с объединениями, несмотря на ажиотаж. Ваши пользователи могут определять столько атрибутов, сколько они хотят (или вы позволите) на длинах, которыми не сложно управлять, пока вы не достигнете около 4 МБ.
Если у вас есть данные, для которых требуется целостность на уровне ACID, вы можете рассмотреть возможность разделения решения, когда данные с высокой степенью целостности хранятся в вашей реляционной базе данных, а динамические данные - в нереляционном хранилище.
источник
Даже если вы предоставляете пользователю добавление пользовательских столбцов, это не обязательно означает, что запросы к этим столбцам будут работать хорошо. Есть много аспектов, которые входят в дизайн запросов, которые позволяют им работать хорошо, наиболее важным из которых является правильная спецификация того, что должно храниться в первую очередь. Таким образом, по сути, хотите ли вы позволить пользователям создавать схемы, не задумываясь о спецификациях, и иметь возможность быстро получать информацию из этой схемы? Если это так, то вряд ли любое такое решение будет хорошо масштабироваться, особенно если вы хотите позволить пользователю выполнять численный анализ данных.
Опция 1
IMO, этот подход дает вам схему без знания того, что означает схема, которая является рецептом для катастрофы и кошмаром для разработчиков отчетов. То есть вы должны иметь метаданные, чтобы знать, в каком столбце хранятся какие данные. Если эти метаданные будут испорчены, у них есть возможность связать ваши данные. Кроме того, это позволяет легко поместить неправильные данные в неправильный столбец. («Что? String1 содержит название монастыря? Я думал, что это были любимые наркотики Чали Шин».)
Вариант 3,4,5
ИМО, требования 2, 3 и 4 исключают любые вариации EAV. Если вам нужно запросить, отсортировать или выполнить вычисления на основе этих данных, EAV - это мечта Ктулху и кошмар вашей команды разработчиков и администратора баз данных. EAV создаст узкое место с точки зрения производительности и не обеспечит целостность данных, необходимую для быстрого доступа к необходимой информации. Запросы быстро превратятся в кросс-таблицу гордиевых узлов.
Вариант 2,6
Это действительно оставляет один выбор: собрать спецификации, а затем построить схему.
Если клиенту нужна максимальная производительность для данных, которые он хочет сохранить, ему необходимо пройти через процесс работы с разработчиком, чтобы понять его потребности, чтобы они сохранялись максимально эффективно. Он по-прежнему может храниться в таблице отдельно от остальных таблиц с кодом, который динамически создает форму на основе схемы таблицы. Если у вас есть база данных, которая допускает расширенные свойства для столбцов, вы даже можете использовать их, чтобы помочь построителю форм использовать красивые метки, всплывающие подсказки и т. Д., Чтобы все, что было необходимо, - это добавить схему. В любом случае, для эффективного создания и запуска отчетов данные должны храниться надлежащим образом. Если в рассматриваемых данных будет много нулей, в некоторых базах данных будет возможность хранить информацию такого типа. Например,
Если бы это был только пакет данных, для которого не нужно было выполнять анализ, фильтрацию или сортировку, я бы сказал, что некоторые вариации EAV могут помочь. Однако, учитывая ваши требования, наиболее эффективным решением будет получение правильных спецификаций, даже если вы храните эти новые столбцы в отдельных таблицах и динамически формируете формы из этих таблиц.
Редкие колонны
источник
Согласно моим исследованиям, несколько таблиц на основе типа данных не помогут вам в производительности. Особенно, если у вас есть объемные данные, такие как записи 20K или 25K с 50+ UDF. Производительность была худшей.
Вы должны пойти с одной таблицей с несколькими столбцами, такими как:
источник
Это проблемная ситуация, и ни одно из решений не кажется «правильным». Однако вариант 1, вероятно, является лучшим как с точки зрения простоты, так и с точки зрения производительности.
Это также решение, используемое в некоторых коммерческих приложениях предприятия.
РЕДАКТИРОВАТЬ
другой вариант, который доступен сейчас, но не существовал (или, по крайней мере, не вступил в силу), когда был задан оригинальный вопрос, - это использовать поля json в БД.
многие реляционные БД теперь поддерживают поля на основе json (которые могут включать в себя динамический список подполей) и позволяют запрашивать их
Postgress
MySQL
источник
У меня был опыт или 1, 3 и 4, и все они заканчиваются беспорядочно, поскольку неясно, что это за данные, или действительно сложно с какой-то мягкой категоризацией, чтобы разбить данные на динамические типы записей.
Я хотел бы попробовать XML, у вас должна быть возможность применять схемы к содержимому XML для проверки набора данных и т. Д., Что поможет хранить различные наборы данных UDF. В новых версиях SQL-сервера вы можете индексировать поля XML, что должно способствовать повышению производительности. (см., например, http://blogs.technet.com/b/josebda/archive/2009/03/23/sql-server-2008-xml-indexing.aspx )
источник
Если вы используете SQL Server, не пропустите тип sqlvariant. Это довольно быстро и должно делать вашу работу. Другие базы данных могут иметь что-то подобное.
Типы данных XML не так хороши с точки зрения производительности. Если вы выполняете вычисления на сервере, вам постоянно приходится десериализовать их.
Вариант 1 звучит плохо и выглядит грубовато, но с точки зрения производительности это может быть вашим лучшим выбором. Я создал таблицы со столбцами с именем Field00-Field99 и раньше, потому что вы просто не можете превзойти производительность. Возможно, вам также придется учитывать производительность INSERT, и в этом случае это тоже самое. Вы всегда можете создать представления для этой таблицы, если хотите, чтобы она выглядела аккуратно!
источник
SharePoint использует вариант 1 и имеет разумную производительность.
источник
Я успешно справлялся с этим в прошлом, не используя ни один из этих вариантов (вариант 6? :)).
Я создаю модель для пользователей, с которыми можно поиграть (сохранить в формате xml и представить с помощью пользовательского инструмента моделирования) и из таблиц и представлений, сгенерированных моделью, чтобы объединить базовые таблицы с пользовательскими таблицами данных. Таким образом, каждый тип будет иметь базовую таблицу с основными данными и пользовательскую таблицу с определенными пользователем полями.
Возьмите документ в качестве примера: типичные поля: имя, тип, дата, автор и т. Д. Это будет указано в основной таблице. Затем пользователи будут определять свои собственные специальные типы документов со своими собственными полями, такими как contract_end_date, renewal_clause, бла-бла-бла. Для этого определенного пользователем документа будет таблица базового документа, таблица xcontract, объединенная по общему первичному ключу (поэтому первичный ключ xcontracts также является внешним по отношению к первичному ключу базовой таблицы). Затем я бы сгенерировал представление, чтобы обернуть эти две таблицы. Производительность при запросах была быстрой. дополнительные бизнес-правила также могут быть встроены в представления. Это сработало очень хорошо для меня.
источник
Наша база данных поддерживает SaaS-приложение (программное обеспечение службы поддержки), где пользователи имеют более 7 тыс. «Пользовательских полей». Мы используем комбинированный подход:
(EntityID, FieldID, Value)
таблица для поиска данныхentities
таблице, в котором хранятся все значения сущностей, используемые для отображения данных. (таким образом, вам не нужен миллион JOIN для получения значений значений).Вы могли бы далее разделить # 1, чтобы иметь «таблицу на тип данных», как предлагает этот ответ , таким образом, вы даже можете индексировать свои UDF.
PS Пару слов в защиту подхода «Entity-Attribute-Value», который все продолжают ругать. Мы использовали # 1 без # 2 в течение десятилетий, и это работало просто отлично. Иногда это деловое решение. У вас есть время, чтобы переписать ваше приложение и перепроектировать БД, или вы можете бросить пару долларов на облачные серверы, которые действительно дешевы в наши дни? Кстати, когда мы использовали подход № 1, наша БД содержала миллионы сущностей, к которым обращались 100 тысяч пользователей, а двухъядерный 16-Гбайт сервер на 16 Гб работал нормально.
источник
custom_fields
таблица, в которой хранятся значения, такие как 1 =>last_concert_year
, 2 =>band
, 3 =>,music
а затемcustom_fields_values
таблица со значениями 001, 1, 1976 002, 1, 1977 003, 2,Iron Maiden
003, 3 ,Metal
Надежда пример имеет смысл к вам и извиняюсь за форматирование!bands
таблица со строкой,1,'Iron Maiden'
затемcustom_fields
со строками, а1,'concert_year' | 2,'music'
затемcustom_fields_values
со строками1,1,'1977'|1,2,'metal'
В комментариях я видел, что вы говорите, что поля UDF должны выводить импортированные данные, которые не отображаются должным образом пользователем.
Возможно, другой вариант - отследить количество пользовательских функций, созданных каждым пользователем, и заставить их повторно использовать поля, сказав, что они могут использовать 6 (или некоторые другие одинаково произвольно ограниченные) верхние значения пользовательских полей.
Когда вы сталкиваетесь с такой проблемой структурирования базы данных, часто лучше вернуться к базовому дизайну приложения (в вашем случае - системе импорта) и наложить на него еще несколько ограничений.
Теперь, что бы я сделал, это вариант 4 (РЕДАКТИРОВАТЬ) с добавлением ссылки на пользователей:
Теперь убедитесь, что вы создали представления, чтобы оптимизировать производительность и правильно настроить индексы. Этот уровень нормализации уменьшает размер базы данных, но делает ваше приложение более сложным.
источник
Я бы порекомендовал # 4, поскольку этот тип системы использовался в Magento, которая является высоко аккредитованной платформой CMS для электронной коммерции. Используйте одну таблицу для определения ваших пользовательских полей, используя fieldId и метки столбцов. Затем имейте отдельные таблицы для каждого типа данных, и в каждой из этих таблиц есть индекс, который индексирует по fieldId и столбцам значений типа данных . Затем в своих запросах используйте что-то вроде:
На мой взгляд, это обеспечит наилучшую производительность для пользовательских типов.
Исходя из своего опыта, я работал на нескольких веб-сайтах Magento, которые обслуживают миллионы пользователей в месяц, размещают тысячи продуктов с настраиваемыми атрибутами продуктов, а база данных легко справляется с нагрузкой, даже для отчетов.
Для создания отчетов вы можете использовать их
PIVOT
для преобразования значений меток таблицы Fields в имена столбцов, а затем сводить результаты запроса из каждой таблицы типов данных в эти столбцы.источник