Как хранить данные с динамическим числом атрибутов в базе данных

У меня есть несколько разных объектов с различным количеством атрибутов. До сих пор я сохранил данные в файлах XML, которые легко позволяют постоянно меняющееся количество атрибутов. Но я пытаюсь переместить его в базу данных.

Каким будет ваш предпочтительный способ хранения этих данных?

Несколько стратегий, которые я определил до сих пор:

  • Наличие одного поля с именем "attributes" в таблице объектов и хранения данных, сериализованных или json'ed там.
  • Сохранение данных в двух таблицах (объектах, атрибутах) и использование третьего для сохранения отношений, что делает его истинным отношением n: m. Очень чистое решение, но, возможно, очень дорогое для извлечения целого объекта и всех его атрибутов.
  • Идентификация атрибутов, которые имеют все объекты, и создание полей для них в таблице объектов. Храните оставшиеся атрибуты в виде сериализованных данных в другом поле. Это имеет преимущество перед первой стратегией, упрощая поиск.

Любые идеи?

Ответы

Ответ 1

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

Я бы выбрал ваш второй выбор. Иметь список атрибутов в таблице атрибутов, объекты в их собственной таблице и таблицу отношений "многие ко многим", называемые атрибутами объекта.

Например:

objects:
    object_id    integer
    object_name  varchar(20)
    primary key  (object_id)
attributes:
    attr_id      integer
    attr_name    varchar(20)
    primary key  (attr_id)
object_attributes:
    object_id    integer  references (objects.object_id)
    attr_id      integer  references (attributes.attr_id)
    oa_value     varchar(20)
    primary key (object_id,attr_id)

Ваша озабоченность по поводу производительности отмечена, но, по моему опыту, разделить столбец всегда дороже, чем объединить несколько столбцов. Если выясняется, что есть проблемы с производительностью, вполне допустимо сломать 3NF по соображениям производительности.

В этом случае я бы сохранил его таким же образом, но также имел бы столбец с необработанными сериализованными данными. При условии, что вы используете триггеры вставки/обновления для синхронизации столбчатых и комбинированных данных, у вас не возникнет никаких проблем. Но вы не должны беспокоиться об этом, пока не возникнет реальная проблема.

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

Ответ 2

Вариант вашего решения 2d - это всего две таблицы (при условии, что все атрибуты имеют один тип):

T1: | столбцы данных объекта | Object_id |

T2: | Идентификатор объекта | атрибут_имя | значение атрибута | (уникальный индекс для первых 2 столбцов)

Это еще более эффективно в сочетании с третьим решением, например. все общие поля переходят в T1.

Sstuffing > 1 атрибут в тот же blob не рекомендуется - вы не можете фильтровать по атрибутам, вы не можете эффективно обновлять их

Ответ 3

Позвольте мне дать некоторую конкретность тому, что говорил DVK.

Предполагая, что значения имеют одинаковый тип, таблица выглядела бы (удачи, я чувствую, что вам это понадобится):

dynamic_attribute_table
------------------------
id         NUMBER
key        VARCHAR
value      SOMETYPE?

пример (автомобили):

|id|    key   |   value   |
---------------------------
| 1|'Make'    |'Ford'     |
| 1|'Model'   |'Edge'     |
| 1|'Color'   |'Blue'     |
| 2|'Make'    |'Chevrolet'|
| 2|'Model'   |'Malibu'   |
| 2|'MaxSpeed'|'110mph'   |

Таким образом,
объект 1 = {('Make', 'Ford'), ('Model', 'Edge'), ('Color', 'Blue')}
и,
entity 2 = {('Make', 'Chevrolet'), ('Model', 'Malibu'), ('MaxSpeed', '110mph')}.

Ответ 4

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

Сериализованный подход, вероятно, самый быстрый (в зависимости от кода для де-сериализации), но это означает, что вы не сможете запросить данные с помощью SQL. Если вы говорите, что вам не нужно запрашивать данные с помощью SQL, то я согласен с @longneck, возможно, вам следует использовать стиль db key/value вместо реляционного db.

EDIT - читайте больше своих комментариев, ПОЧЕМУ вы переходите на db, если скорость ваша главная проблема. Что BAD о вашей текущей реализации XML?

Ответ 5

Я использовал эту схему:

t_class (id RAW(16), parent RAW(16)) -- holds class hierachy.
t_property (class RAW(16), property VARCHAR) -- holds class members.
t_declaration (id RAW(16), class RAW(16)) -- hold GUIDs and types of all class instances
t_instance (id RAW(16), class RAW(16), property VARCHAR2(100), textvalue VARCHAR2(200), intvalue INT, doublevalue DOUBLE, datevalue DATE) -- holds 'common' properties

t_class1 (id RAW(16), amount DOUBLE, source RAW(16), destination RAW(16)) -- holds 'fast' properties for class1.
t_class2 (id RAW(16), comment VARCHAR2(200)) -- holds 'fast' properties for class2
--- etc.

RAW(16) находится где Oracle содержит GUID s

Если вы хотите выбрать все свойства для объекта, вы выдаете:

SELECT  i.*
FROM    (
        SELECT  id 
        FROM    t_class
        START WITH
                id = (SELECT class FROM t_declaration WHERE id = :object_id)
        CONNECT BY
                parent = PRIOR id
        ) c
JOIN    property p
ON      p.class = c.id
LEFT JOIN
        t_instance i
ON      i.id = :object_id
        AND i.class = p.class
        AND i.property = p.property

t_property удерживайте вещи, которые обычно не выполняются (например, текстовые описания и т.д.)

Быстрые свойства - это фактически обычные таблицы, которые у вас есть в базе данных, чтобы сделать запросы эффективными. Они хранят значения только для экземпляров определенного класса или его потомков. Это делается для того, чтобы избежать дополнительных объединений.

Вам не нужно использовать быстрые таблицы и ограничить все ваши данные этими четырьмя таблицами.

Ответ 6

звучит так, как будто вам нужно что-то лизать couchdb, а не RDBMS.

Ответ 7

если вы собираетесь редактировать/манипулировать/удалять атрибуты в более поздней точке, в результате будет выполняться истинный n: m (второй вариант). (Или попробуйте сделать 2 таблицу, где повторяется тот же атрибут. Но размер данных будет высоким)

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

Ответ 8

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

В противном случае СУБД может не подходить наилучшим образом. Что-то вроде НЕТ SQL может работать лучше.

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

Особенно, если вы хотите подключить их с помощью первичных ключей. Это также помогает навести порядок и здравомыслие, если у вас есть таблицы "Продукты", "Заказы", "Клиенты" и т.д. Вместо таблиц "Объекты и атрибуты".

Тогда посмотрите на свои атрибуты. Все, что существует более чем, скажем, для 50% объектов в этой категории типов, делает его столбцом в таблице объектов и использует null, когда оно не используется.

Все, что является обязательным, должно, конечно, быть определено как столбец NOT NULL.

В остальном у вас может быть одна или несколько таблиц "дополнительных атрибутов".

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

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

В этом случае вы можете сделать что-то вроде:

MainObjectTable:
  mainObjectId: PRIMARY KEY
  columns...
MainObjectVariant1Table:
  mainObjectId: FOREIGN KEY TO MainObjectTable
  variant1Columns...
MainObjectVariant2Table:
  mainObjectId: FOREIGN KEY TO MainObjectTable
  variant2Columns...

Я думаю, что тяжелая работа, которая в конечном итоге окупится, состоит в том, чтобы проанализировать данные, найти объекты и часто используемые атрибуты и превратить их в хорошую модель "объект/ERD/DB".