Схема для поддержки динамических свойств - PullRequest
17 голосов
/ 16 января 2011

Я работаю над редактором, который позволяет его пользователям создавать определения «объектов» в режиме реального времени. Определение может содержать ноль или более свойств. Свойство имеет имя тип. После создания определения пользователь может создать объект этого определения и установить значения свойств этого объекта.

Таким образом, одним нажатием кнопки мыши пользователь должен т.е. быть в состоянии создать новое определение под названием «Велосипед» и добавить свойство «Размер» типа «Числовой». Затем другое свойство с именем «Имя» типа «Текст», а затем другое свойство с именем «Цена» типа «Числовой». Как только это будет сделано, пользователь сможет создать пару объектов «Велосипед» и заполнить значения свойств «Имя» и «Цена» каждого велосипеда.

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

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

Table obj_defs

id | name      |
----------------
 1 | "Bicycle" |
 2 | "Book"    |

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

Table prop_defs

id | obj_def_id | name      | type |
------------------------------------
 1 |          1 | "Size"    |    ? |
 2 |          1 | "Name"    |    ? |
 3 |          1 | "Price"   |    ? |
 4 |          2 | "Title"   |    ? |
 5 |          2 | "Author"  |    ? |
 6 |          2 | "ISBN"    |    ? |

Мне также нужна таблица, содержащая каждый объект:

Table objects

id | created    | updated    |
------------------------------
 1 | 2011-05-14 | 2011-06-15 |
 2 | 2011-05-14 | 2011-06-15 |
 3 | 2011-05-14 | 2011-06-15 |

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

Table prop_vals

id | prop_def_id | object_id | numeric | textual | boolean |
------------------------------------------------------------
 1 |           1 |         1 |      27 |         |         |
 2 |           2 |         1 |         |  "Trek" |         |
 3 |           3 |         1 |    1249 |         |         |
 4 |           1 |         2 |      26 |         |         |
 5 |           2 |         2 |         |    "GT" |         |
 6 |           3 |         2 |     159 |         |         |
 7 |           4 |         3 |         |    "It" |         |
 8 |           5 |         3 |         |  "King" |         |
 9 |           6 |         4 |       9 |         |         |

Если бы я реализовал эту схему, что бы содержал столбец "type" таблицы prop_defs? Целые числа, которые каждый сопоставляет с именем столбца, varchars, которые просто содержат имя столбца? Любые другие возможности? Поможет ли мне здесь хранимая процедура? И как будет выглядеть SQL для получения свойства name объекта 2?

Ответы [ 2 ]

28 голосов
/ 16 января 2011

Вы реализуете модель с именем Entity-Attribute-Value http://en.wikipedia.org/wiki/Entity-attribute-value_model.

Многие люди скажут, что это плохая идея (обычно я один из них), потому что ответ на ваш последний вопрос: «Какой SQL-запрос для извлечения ...» имеет тенденцию быть «густым волосатым и противным, и получать хуже. "

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

По первому вопросу: «Что будет в столбце« type »таблицы prop_defs», все будет проще, если у вас есть таблица типов и описаний, которая содержит {«numeric», «Any Number»}, { «textual», «String»} и т. д. Первое значение - это первичный ключ. Затем в prop_defs ваш столбец «тип» является внешним ключом для этой таблицы и содержит значения «числовой», «текстовый» и т. Д. Некоторые скажут вам неверно всегда использовать целочисленные ключи, потому что они быстрее присоединяются, но если вы используете значения » числовые "," текстовые "и т. д. вам не нужно присоединяться , а самый быстрый JOIN - это тот, который вы не делаете.

Запрос на получение одного значения будет иметь оператор CASE:

SELECT case when pd.type = "numeric" then pv.numeric
            when pd.type = "textual" then pv.textual
            when pd.type = "boolean" then pv.boolean
  from prov_vals pv 
  JOIN prop_defs pd ON pv.prop_def_id = pv.id
 WHERE pv.object_id = 2
   AND pd.name = "Name"
4 голосов
/ 17 января 2011

Вы должны согласиться с тем, что реляционные базы данных не способны обеспечить такую ​​функциональность. Они МОГУТ это обеспечить, но не очень хороши в этом. (Надеюсь я ошибаюсь). Реляционные базы данных лучше подходят для определенных интерфейсов, а не для изменения интерфейсов.

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

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

- Как правило, вы создаете / изменяете таблицы во время разработки, а не во время выполнения. Должны ли мы действительно дискриминировать изменение базы данных во время выполнения? может, может и нет. Создание новых таблиц, внешних ключей и столбцов во время выполнения может привести к созданию настоящих динамических объектов, в то же время обеспечивая преимущества производительности «обычных» таблиц. Но вам придется запросить схему базы данных, а затем динамически генерировать все ваши запросы. Это было бы отстой. Это полностью нарушило бы концепцию таблиц как интерфейса.

...