Как хранить данные с динамическим количеством атрибутов в базе данных - PullRequest
18 голосов
/ 18 сентября 2009

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

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

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

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

Есть идеи?

Ответы [ 7 ]

22 голосов
/ 18 сентября 2009

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

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

Например:

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 по соображениям производительности.

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

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

6 голосов
/ 18 сентября 2009

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

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

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

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

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

3 голосов
/ 18 сентября 2009

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

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

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'   |

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

2 голосов
/ 18 сентября 2009

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

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 с

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

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 содержит материалы, которые вы обычно не ищете (например, текстовые описания и т.

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

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

2 голосов
/ 18 сентября 2009

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

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

РЕДАКТИРОВАТЬ - читая больше ваших комментариев, ПОЧЕМУ вы переходите на дБ, если скорость вашей основной заботы. Что плохого в вашей текущей реализации XML?

1 голос
/ 18 сентября 2009

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

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

1 голос
/ 18 сентября 2009

звучит так, будто вам нужно что-то лизать couchdb , а не СУРБД.

...