Варианты проектирования БД

SQL*
Все люди, вовлеченные в проектирование различных БД, думаю, нередко задаются вопросом о нужной структуре. На данный момент, есть два варианта хранения данных, каждый из которых, в свою очередь, имеет ряд своих недостатков.

1. Объединенное хранение

Например, есть таблица типов объектов (ObjectsTypes), таблица самих объектов (Objects) и их свойств (ObjectsFields). По желанию, можно хранить еще и типы полей-свойств, это не принципиально.
Связи между таблицами определены однозначно (объект имеет один тип (typeID) и ряд свойств, связанных с родительским объектом полем objectID), между объектами связь осуществляется и с помощью древовидной структуры (родитель ← ребенок) и путем заведения отдельной таблицы (ObjectsRelations) для сетевой структуры, в которой дочерний элемент может иметь несколько родительских.

2. Индивидуальное хранение

Если представлять эту реализацию на примере, то для хранения блогов нужна таблица Blogs с полями, относящимися к нему, таблица BlogsTopics, хранящая посты и их свойства, таблица BlogsVotes, содержащая все пользовательские голоса и т.д. Можно до бесконечности развивать этот пример — смысл такого хранения в том, что для каждого типа данных создается своя таблица (если нужно, то несколько).

Я считаю, что для индивидуальных решений, например, для системы Хабры, идеально подошел бы второй вариант, а первый можно использовать в коммерческих решениях (как, собственно, многие и делают).
Хотелось бы услышать неозвученные мной доводы в пользу каждого из методов.
+22
26 июня 2008, 03:12
53
Gunger 16,4

комментарии (51)

0
megahertz #
Если я правильно понял мысль, то это просто 2 разные нормальные формы таблиц.
0
Gunger #
Да, разумеется. Просто хочется услышать плюсы и минусы каждой из реализаций. Одни из самых явных — на добавление нового типа объекта в первом варианте будет портачено гораздо меньше времени, чем во втором (быстрая расширяемость).
НЛО прилетело и опубликовало эту надпись здесь
0
Gunger #
Из коммерческих CMS такую реализацию выбрали UMI.CMS и Netcat, поэтому, изучив эти системы, мне и пришло в голову посоветоваться с Хабрасообществом. Спасибо за совет ;-).
0
Darkened #
Пред-рас-судки.
«Уровень требуемой квалификации» — вот именно. Это не ущербная попытка. Это не каждому быдлокодеру доступный вариант.
+4
bat #
Если мы говорим о реляционных БД, то первый вариант имеет очень ограниченное применение. Да это универсально, но за универсальность надо платить, в данном случае, производительностью.
0
isapioff #
присоединяюсь к Вашему мнению.
0
Caesar #
более того, если мы говорим о первом варианте, нас совершенно не интересует, является ли СУБД реляционной.
0
Cepg #
Если хочется объектную бд, то нужно пользоваться хотя бы ORDMS (объектно-реляционная) PostgreSQL...
0
genbit #
В первом случае вы, как мне кажется, описываете так называемую EAV модель, или модель Тенцера. Недостатки очевидны - медленные INSERT, UPDATE, DELETE. Необходимость реализовывать самому проверку связей между объектами, интерфейс по управлению свойствами объектов, типов, и т.д. Плюсов тоже не мало, это почти(!) универсально, но не так производительно, как во втором случае. Мне кажется тут надо смотреть, для чего конкретно разрабатывается БД.
0
Gunger #
Я же привел пример — разработка системы, подобной Хабрахабру.
0
megahertz #
при разработке конкретного проекта конечно предпочтительней второй вариант, реализация проще и скорость выше. первый больше подходит при разработке универсальной платформы
0
Gunger #
Спасибо. Я сам пришел к похожему умозаключению.
0
el777 #
А затем появятся объекты, которые как и туда и сюда подходят. Грубо говоря были Новости и Статьи, к ним добавили Анонсы - куда их - в отдельную таблицу?
Затем придут теги - и все сущности придется как-то перевязывать между собой, то есть к каждой таблице сущностей добавится еще таблица-связка с тегами.
0
Gunger #
Теги, комментарии — это настолько обобщенные сущности, что их спокойно можно хранить каждый в одной таблице, привязывая к объектам с помощью идентификатора (objectID) и типа (objectType).
Про «Новости, Статьи, Анонсы» — детальная схожесть этих трех объектов взывает к хранению их в одной таблице, с разными типами (поле type).
0
el777 #
А как быть со внешними ключами в этой схеме?
0
Gunger #
А что если внешним ключом назначить связку objectType и objectID?
0
el777 #
Не очень понял как это можно сделать. Колонка должна ссылаться на колонку в другой таблице. Можно ли ссылаться сразу на несколько таблиц?
0
afterdark #
Не, не может, насколько я знаю. Потому единственный вариант, что могу предложить: таблицу ObjectUniqueIds и ссылки на нее из всех таблиц, к которым можно привязать теги и комменты. Тогда внешний ключ реализуется в два этапа: ObjectID -> ObjectUniqueID -> Comments|Tags. Но тут, конечно придется генерить UniqueID вручную (триггером).

Кстати, msSQL есть такая штука как UID - тип данных такой. Он ваще уникальный.
0
el777 #
Еще одна связка? Чем сложнее, тем медленне это все будет работать.
А вот вариант с UID - очень интересен.
Тоже самое можно сделать, например, в постгрес - создав общую последовательность для всех таблиц.
0
alpass #
Первый вариант похож на систему, расчитанную под будующую (считаем неизвестную на момент разработки) сильную кастомизацию. Один из таких известнейших продуктов - 1С.

http://www.oracle.com/global/ru/oramag/august2004/russia_gen_abramov.htm

В статье дано описание Remedy, это не суть важно, легко можно подставить туда и "пункт 1".
+1
RVK #
Я знаю третий. Как только кармы хватит напишу статью по этому поводу.
+1
Gunger #
Поднял карму. Можете написать в личный блог.
0
RVK #
Спасибо :) Попробую до футбола выделить время на статью. Моя(?) идея как раз для социальных подходит лучше всего.
0
el777 #
Пишите, очень интересно.
0
JustLuckyGuy #
Сорри за оффтоп, но: а хабракармаэффект уже не работает? Раньше за просьбы кармы жестоко расплачивались.
+4
el777 #
ОФФТОП: Я думаю, если человек просит на дело - статью по интересующей тебя теме, то ничего плохого в этом не вижу. От этого только все выиграют.
+3
RVK #
Прошу прощения, я тут новенький :)
Статья готова http://rvk.habrahabr.ru/blog/45259.html
Надеюсь достойная плата за карму.
0
prolis #
1 вариант использутся при гибкой по своей природе структуре таблиц
За второй вариант вас зато никогда не побьют последующие разработчики
0
mg2i #
Можно предложить еще "третий" вариант: настройщик описывает сущности и связи в какой-либо нотации, а программа затем "умно" генерирует объекты БД (таблицы, хранимки...)по второму варианту.
Выйдет очередной конструктор а-ля 1С :)
0
el777 #
По сути отдать проектирование системы на откуп роботу? :)
0
esobchenko #
Все это объединено дисциплиной ORM (Object-Relational Mapping) и хорошо описано на разных ресурсах. Способов представления объектов немного больше чем 2. Вот, например, есть такая статья: http://www.agiledata.org/essays/mappingObjects.html
0
Bakti9rov #
в ActiveRecord (RoR) эти свзянности уже давно сформулированы (также как и в LIMB-фреймворке, предлагающем свой вариант ActiveRecord)

has_one - связанность один объект к одному
belongs_to - то же самое, но с foreign_key
has_many - множество к одному
has_many_and_belongs_to - один к множеству через дополнительную мета-таблицу
+1
MikhailEdoshin #
На мой взгляд, первый вариант — типичная ошибка проектировщика, стремящегося к универсальности. Сколько я видел обобщенных систем в своей сфере (автоматизация для малого и среднего бизнеса), это всегда было в десять раз больше работы и в десять раз более сложный код, при практически идентичном результате. Это не решение, а перекладывание проблемы в другое место; как в RFC 1925:

(6) It is easier to move a problem around (for example, by moving the problem to a different part of the overall network architecture) than it is to solve it.

(6a) (corollary). It is always possible to add another level of indirection.


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

Правильнее второй метод, только не в точности таком виде. Каждый тип данных может хранится в отдельной таблице, но ведь он может и «включать в себя» другие типы данных. Если есть статьи, посты в блоге, новости и у них есть общие свойства, например, ко всем можно добавлять комментарии, то можно вынести это общее в отдельную таблицу, скажем, «сообщений», и добавлять комментарии к каждому сообщению. В данном случае «сообщение» получается абстрактным типом, которой сам по себе не используется, но входит в состав каждой статьи, поста и новости; они от него как бы «наследуют», хотя наследование термин не очень удачный. Предположим, что за новости не голосуют, а за статьи и посты можно; в таком случае статьи и посты будут ссылаться на хранящийся отдельно «рейтинг», и голоса будут добавляться к рейтингу, а не к статьям. В таком случае, чтобы начать голосовать за новости, достаточно будет добавить в таблицу ссылку на рейтинг. И т. д.
0
zen #
Для хранение объектов с похожей структурой годится наследование, в котором основная таблица
содержит общие для всех атрибуты, такие как id, pub_date, mod_date, author_id ...., а
остальные таблица наследуя ее, добавляют свои атрибуты. При этом можно использовать поиск как
по отдельным таблицам, так и по всем (спрашивая мастер таблицу). В PostgreSQL это реализуется
весьма просто. Есть еще способ в PostgreSQL - иметь одну таблицу с общими атрибутами, а всю
специфику вынести в отдельное поле типа перлового хэша.
0
el777 #
Да, структура получается очень гибкой, но есть проблема в обработке. Комфортно с ней можно работать только из Perl-а. Когда система развивается, возникает большая потребность в интеграции с другими компонентами, которые могут быть написаны на других языках. И вот там распарсивание хеша может стать проблемой, приходится лепить костыли: парсить руками, либо писать специальные хранимые процедуры для вытаскивания определенных полей хеша.
Я бы предложил хранение в виде XML (благо постгрес нормально с ним работает).

Плюсы:
+ очень гибко - в поле можно записать все, что угодно: хоть сложное дерево настроек, хоть массив объектов
+ без проблем обрабатывается приложением на любом языке, а так же самой базой
+ удобная возможность поиска с помощью xpath-запросов
+ для ускорения такого поиска можно построить индекс по конкретным xpath-запросам. В зависимости от особенностей задачи можно использовать разные индексы - впрочем про них вы больше меня расскажите :)

Минусы:
- производительность
0
zen #
Почему только из перла ? В PostgreSQL можно работать c SQL, если пользовать наше расширение hstore (http://www.sai.msu.su/~megera/wiki/Hstor…)
0
el777 #
Интересная вещь. Спасибо!
Как производительность? Индексируемость?
0
Bakti9rov #
Для хранения объектов с множеством (от одного до сотни и далее) свойств еще выгодно использовать мета-таблицы.

Например есть сущность User, есть сущности UserGroup, UserAccess etc. В основной табллице хранится главная информация - login, password, created_at и т.д., и связи с правами и группой. В мета-таблице - UserMeta - хранятся свойства объекта в виде ключ-значение: что то вроде (id, user_id, property, value).

Тогда при выборке свойств объекта будут два запроса - первым этамом выборка информации из главной таблицы и связанных таблиц; вторым этапом выборка мета-свойств объекта.

Кто еще применяет такую же архитектуру? Знаю что в WordPress используется такая же идея (свзянность между posts и posts_meta).
+1
Bakti9rov #
* первым этамом - первым этапом

поднимите карму, напишу подробнее как такая же схема используется в classifieds движках (движках объявлений)
0
el777 #
Сейчас должно хватать для личного блога.
0
Nilf #
Если очень много изменяющихся свойств(меняется струткура ) - выделяют ядро сущности, которое хранится в обычных полях. Все поля, которые могут меняться, загоняют в поле xml. эти свойства вторичны и запросы по ним не выполняются или выполняются очень редко, все активные действия выполняяются над полями ядра. Раньше использовалось clob и udf, которые парсили это поле, с появлением xml все делается гораздо проще и быстрее, стандартными средствами.

Наследование в рсубд практически не используется. Нафига, база должна хранить данные, остальным займется приложение.
0
Bakti9rov #
> Раньше использовалось clob и udf, которые парсили это поле, с появлением xml все делается гораздо проще и быстрее, стандартными средствами.
Ну xml в крайнем случае можно закешировать.

> Наследование в рсубд практически не используется. Нафига, база должна хранить данные, остальным займется приложение.
Согласен, база должна хранить данные, а не модель (модель строится на основе этих данных + поведении). Маппингом данных и сущностей занимается ORM.

Насчет выделения какого-то ядра у сущности - несколько туманная идея... можете привести примеры?
Каковы плюсы от проектировать БД на основе частоты изменения полей?
0
Nilf #
>Насчет выделения какого-то ядра у сущности - несколько туманная идея... можете >привести примеры?
>Каковы плюсы от проектировать БД на основе частоты изменения полей?
Я приводил решение для случая когда у сущности много изменяющихся полей. Например у кредитной истории могут появляться дополнительные атрибуты необходимые для каких-то скоринговых целей. Основные атрибуты КИ( субъект, документы, адрес, данные бюро) уже определены и не меняются. Просто все поля, которые планируются в будущем видоизменть или добавлять, добавляются в структуру xml. В результате никаких мета-таблиц не надо создавать, определяете схему документа и вперед. Так удобнее. Во первых у старые версии сущности не будут обрастать не используемыми полями, функционалом. И контроль версий естественно лучше. Программа может спокойно парсить сущность по старой xsd, а новые записи по новой. И все это хранится в одной таблице, где количество колонок фикисровано. По произовдительности я думаю даже выигрыш будет против многослойных джойнов.
НЛО прилетело и опубликовало эту надпись здесь
0
plagi #
Работали над проектом автоматизации финансового блока компании.
Архитектором выбрал первый вариант, в связи с планировавшейся большой универсальностью и подключением большого числа модулей других подразделений компании.
В итоге все это вылилось в огромный геморрой при работе с данными и пришлось все упрощать и отпиливать напильником в сторону второго варианта.
0
Denisio #
Такой вариант хорош если:
1) объектов много и взаимосвязи между ними также должны быть динамическими;
2) типы объектов должны создаваться не программистом, а неким дизайнером или внедренцем;
3) структура тщательно продумана архитектором с опытом работы с такими системами;

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

Но есть и очевидные минусы. Сильно бОльшая сложность разработки и тестирования, большое (иногда огромное) количество бизнес логики в БД (триггеры, процедуры, констрейнты и прочие) и часто меньшую скорость работы за счет универсальности.
0
bmc #
Хочу прокомментировать первый вариант - сейчас на работе с ним приходиться иметь дело...

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

Присоединюсь ко всем здесь высказывавшимся - работать с этой пачкой говна очень сложно. Либо полностью через хранимые процедуры (которые возвращают тебе строку с полями, которые ты запросил), либо приходится делать УЖАСНЫЕ извращения на клиенте. В частности ужасной ужастностью здесь является известная N+1 проблема, из-за которой динамическая lazy-линковка практически невозможно.
0
beskov #
1. Это архитектурный паттерн БД EAV (Entity-Attributes-Values).
2. Это архитектурный паттерн БД Entities and External Domains (название моё, модель по-умолчанию для большинства учебных и малых схем).

Есть ещё:
3. Core Entities & Hiers (Ключевые сущности и наследники, очень мне нравится в последнее время).
4. DW (Facts & Dimensions) — классика аналитических систем.
5. Регистры — хорошее решение для учётных систем.

Кое-какие детали есть здесь: http://DatabaseDesignPatterns.org/index.…
0
nod #
Спасибо за информацию. Хоть что-то полезное нашлось.
0
sosedoff #
первый вариант удобно использовать для мелочей, типа «Просмотры», «Рейтинг».

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.