Pull to refresh

Comments 66

я юзаю первый с изменениями. тип в целом, параметры отдельной таблицей (object_id, param, value).
Так проще организовать фабрику в коде для сериализации объектов, проще добавлять/удалять новые типы объектов, ну и проще оперировать, написав единый интерфейс.
Во втором случае сложности с добавлением новых типов, на каждый новый тип свои функции и классы.
Вобщем, это классика для почитателей Александреску :)
Работа с такой струтурой возможна только в языках, поддерживающих неопределенные типы переменных. Например, PHP.
Это не совсем верно. В С++ тоже можно отлично работать с такими БД ;)
Не уверен на счет всех, но в некоторых БД есть функция GUID или UUID. Она возвращает уникальный идентификатор. Можно в 3м варианте использовать его вместе с обычным autoincrement ID. Тогда не придется следить за пересечением последних.

Если думать конкретно о проблеме с координатами и ID, то мне очень хочется завести отдельные таблицы конкретно для координат и для ID, а в таблицах кораблей, планет и чужих просто хранить ссылку(Foreign Key).
Ну первые мысли такие:
Используй 2-й метод, но не мудри с ID.
Если тебе надо найти по координатам, то объединяй (JOIN) все таблицы и ищи.
Если хочешь искать по ID, то ты наверняка знаешь какой объект ищешь. Так что перебирать все таблицы не надо.
А вместо "WHERE `x` > a and `x` < b" можно использовать "WHERE x BETWEEN a AND b"
UFO just landed and posted this here
Я бы использовал http://ru.wikipedia.org/wiki/Berkeley_DB
BDB является нереляционной базой данных - она хранит пары ключ/значение как массивы байтов, и поддерживает множество значений для одного ключа

На мой взгляд идеально подойдет под задачу
я бы создал таблицу космос с id, x, y, type и таблицу properties c id, id_element, property_name, property_value

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

Получилось что-то вроде этого:
items - id, name, type
types - id, name
types_attributes (соответствие атрибутов типу товара) - id, type_id, attribute_id
attributes - id, name
attributes_data (собственно, значения атрибутов) - id, item_id, attribute_id, value

Итого имеем 5 таблиц с более-менее удобным поиском по ним, и практически без дублирования :)
есть-ли необходимость в таблице attributes ?
если attribute_id - просто поменять на название в 2х других таблицах...
будет дублирование, лучше его избегать
Может один-ко-многим? Надо же как-то уже правильно выражаться. Один к одному, один ко многим или многие к многим. Другого не дано.

По вопросу - использую 3 таблицы, так удобнее. А оптимизировать тот запрос, что Вы указали - можно - созданием индексов по `x`
Ну это не совсем один-ко-многим.
Если таблиц будет десяток или сотня, то поддержка и развитие кода станет сложной..
При сотне тысяч космических объектов, в таблице будет несколько миллионов записей. Не уверен, что выборка будет работать оптимально быстро :)
Если изначально об этом знать, то тогда конечно. Но не всякая задача требует рассмотрения возможности несколько миллионов записей в таблицу. Ведь тогда и архитектура тоже будет другая..
Вот и пытаемся подобрать архитектуру таблицы, чтобы было несколько сотен тысяч, которые не превращались бы в милионы
Если вам кажется, что в таблице никогда не будет миллионов записей, то вам кажется…

Лучше заранее подумать об этом :-)
Первый вариант можно модифицировать, чтобы можно было делать поиск по полям объектов. Для этого из неё нужно убрать поле variables, а вместо него сделать ещё одну таблицу:

create table 'space_params' (
space_id int,
param_name varchar(50),
param_value text,
constraint param_to_space foreign key (space_od) references 'space'(id)
)

Разумеется, возможны варианты, например имя параметра может браться из словаря (ещё одной таблицы) или можно что-то придумать для полей разных типов, ну и т.п.
На мой взгляд лучше использовать третий из вариантов плюс использование view с операцией union (решается проблема "Необходимо осуществить несколько запросов, при пересечении полей...") или другим объединением данных например конкатенации id и имени таблицы и т.д. в зависимости от решаемой задачи (решается проблема "Необходимо следить за пересечениями по ID между таблицами").
Единственная проблема которую не удасться решить это большое количество таблиц ;)
Обратите внимание на 5й вариант. Я думаю, он будет работать быстрее, чем union :)
А почему не сделать так?
========================

// 0 таблица
CREATE TABLE `space_object` (
ID int(11) NOT NULL auto_increment,
`x` int(11) NOT NULL default '0',
`y` int(11) NOT NULL default '0',
PRIMARY KEY (`ID`)
);
// 1я таблица
CREATE TABLE `ship` (
`ID` int(11) NOT NULL auto_increment,
`space_object_id int(11),
`armor` float NOT NULL default '0',
`maxarmor` float NOT NULL default '0',
...
PRIMARY KEY (`ID`)
)

// 2я таблица
CREATE TABLE `planet` (
`ID` int(11) NOT NULL auto_increment,
`space_object_id int(11),
`radius` float NOT NULL default '0',
...
PRIMARY KEY (`ID`)
);

// 3я таблица
CREATE TABLE `alien` (
`ID` int(11) NOT NULL auto_increment,
`space_object_id int(11),
`damage` float NOT NULL default '0',
...
PRIMARY KEY (`ID`)
);

===========
Плюсы:
- Один запрос на выборку.
- Самое компактное хранение данных
- Возможно поиск по свойствам объектов


P.S. использовать в просто id все таки не правильно. Может лучше platen_id, alien_id и т.д.
1) При запросе по координатам - получается 3 выборки. Сначала получить таблицы для ship, где ship.object_id совпадает с space.ID в заданных координатах. Затем получить таблицы для planet, где planet.object_id совпадает с space.ID в заданных координатах. Ну и тоже самое для alien.object_id.

2) Объеденить все три таблицы с помощью JOIN в один запрос, чтобы БД не обрабатывала запрос по скорости, как 3 select'a, получив объедененную конечную выборку (как во втором примере с объедененными таблицами, с null).
На сколько это будет быстрее, чем 3 select'a?
Это меня зарубает что-то. В общем минус подхода в том, что БД будет делать несколько запросов :)
one-to-many я бы перевел как один-ко-многим
Я не путаю понятие один-ко-многим. Я говорю, чтобы объеденить в одной таблице десяток (возможно сотню) других таблиц. Объеденить, а не связать и не делать выборку :)
а зачем связывать? Нормализация все-таки актуальнее, пока нет больших нагрузок..
Денормализация базы нормальное явление. Часто используется когда в базе предполагается хранить объекты, наследованные от какого-либо общего.
У меня был похожий случай. Я выбрал вариант 3, но в качестве id использовался foreign key id из "общей" таблицы, таким образом id-шники остались уникальны. Кроме того в общей таблице пришлось "кэшировать" часть данных в текстовом виде как в варианте 1. Но сейчас я думаю что возможно лучше было бы хранить всё в одной таблице и не париться. Кстати, во 2 варианте совсем не обязательно добавлять поля для каждого типа объектов, в принципе можно использовать одни и те же поля, если их тип совпадает. Но вообще это всё зависит от ситуации - какие будут запросы.
Я хотел, чтобы в таблице могли разобраться сторонние разработчики - по этому пути с десятками полей в одной таблице (тем более использовать поля под другие меры) не подходят. Тем более, в таблице будет более 100тыс записей, что раздует БД достаточно сильно.
Я использую основную таблицу с общими свойствами и основным ID, на которую посредством foreign key ссылаются таблицы детализирующие разные типы объектов. Кроме того есть таблица описывающая каждый возможный тип объекта, включающая имя таблицы для данного типа и имена VIEW исполльзующихся в общих отчетах. В этих таблицах как PRIMARY KEY используется тот же ключ что и в общей.
В принципе можно сделать VIEW который покажет все поля всех типов данных - только имхо это _никогда_ не нужно. (Что толку ГЛОБАЛЬНО искать если не знаешь, в каком поле искать-то?)
А, и еще один вариант, который используется в том же продукте - это таблица со свойствами объектов (свойств оных много, они все разные, и вообще могут различаться даже для объектов одного типа).
Вот для них используется таблица с тремя основными полями - ID_объекта, ID_свойства, Значение_свойства_в_виде_XML_данных
Последний вариант считаю наиболее подходящим для большинства случаев.
Вообще все сложно тут. Каждую задачу нужно рассматривать отдельно. Тем более вы так хорошо все расписали с плюсами минусами и выводами, что топик можно считать не опросным, а обучающим :)
А он и планировался опросно-оббучающим. Чтобы Хабралюди смогли предложить свои решения.
В SQL Server я бы завёл для параметров поле XML, а целостность и формат соблюдал средствами программы. Для оптимизации поиска посмотрел бы на тему XML Index-ов, может и поможет даже для разнородных объектов.
По крайней мере логи храню только так.
Мне оптимальным кажется наследование объектов, как в Postgres.

И вообще — задача, о которой о сути идёт речь — организация хранения наследования. Рассмотрено, кстати, детально в книге Роберта Мюллера «Базы данных и UML. Проектирование». А не это неуклюжее «один к разным».
Полностью согласен. Если не нужно морочиться с унификацией для использования различных СУБД, то postgres с наследованием таблиц - самое то. Это фактически второй вариант, только с очень удобной поддержкой со стороны СУБД.

http://www.postgresql.org/docs/8.3/stati…
А что там с внешними ключами?
Хороший вопрос. Цитата из документации:

All check constraints and not-null constraints on a parent table are automatically inherited by its children. Other types of constraints (unique, primary key, and foreign key constraints) are not inherited.

Другими словами, первичные, внешние и уникальные ключи для общих полей (которые в базовой таблице) не распространяются на записи дочерних таблиц. Причём даже если продублировать уникальный ключ в дочерней таблице, то это не обеспечит уникальности значений в общей таблице. Короче, есть у них в этом косяк. Говорят, что скорее всего исправят в будущем.
Хотел бы заметить, что в решении "Строковые параметры для объектов" первый минус не распространяется на связку .NET/MS SQL Server/С#/LINQ To SQL. В базе определяем вместо сомнительного text поле xml, которое linq to sql позволит замапить в XML-объект в ORM с полной поддержкой всего нужного вам xml-функционала.

Другое дело, что самым распространенным решением данной проблемы является решение "Параметры объектов в отдельной таблице" и я бы, наверное, использовал его. Но. Есть еще один вариант решения вашей проблемы: разряженные колонки (sparse columns) MS SQL Server 2008, методика, придуманная как раз для ваших нужд, которая позволяет гибко хранить в базе тысячи колонок. Подробно можно почитать в MSDN
Все зависит от тех запросов, на которых будет работать приложение, от них и будет зависит выбираемый вариант. Не зная структуры будущей программы, совета, что лучше, дать сложно. Как вы правильно написали + и - есть везде.

А 3 select'a мне кажутся очень логичным решением. Никто же не думает выбирать, скажем, "книги" и "авторов" одним select'ом.
Из своего опыта работы с разными СУБД могу сказать, что всегда нужно учитывать особенностии конкретной СУБД. Решение SQL задач из общих соображений (без учета особенностей СУБД) к практической реализации имеет мало отношения. Более того в постановке задачи ничего не сказано о профиле данных (частоте изменений свойств обьектов, частоте появления новых обьектов, частоте появления новых типов обьектов и т.д.) Если решать SQL зачади в самом общем виде, то естесвенно надо полагаться на теорию реляционных баз данных, а не выдумывать велосипед. Но в большинстве случаев это не выгодно с точки зрения производительлности.

Примеры:
Postgres:
Особенность: на каждый update поля в строке в действительностии добавляется новая строка, старая остается не видимой для сессии.
Это особенность реализации версионности. Поэтому для Postgres'а оптимально разделять таблицы на более мелкие (вертикальное партицирование). Где в одной будут содержаться редко изменяющиеся поля и таких полей может быть много, в другой часто изменяющиеся и таких полей в одной аблице должно быть как можно меньше. Причем таким образом можно делить даже одну сущность (например "корабль"), что уже не верно с точки зрения реляционной теории.
Oracle:
Особенность: Наличие возможности более гибко влиять на построение плана запроса(oracle hints). Что дает возможность лучше подстраиваться под профиль данных и оптимизировать сложные запросы. Можно выбирать решения больше удовлетворяющие реляционным бд, что дает большую гибкость при выборке данных.
Особенность: наличие очень хорошей имплементации bitmap индексов, сущесвенно ускоряющих выборку по однородным данным и уменьшающим обьем хранимых индексов. Это приводит к необходимости создания буферов. Например в буфферные таблицы с разных клиентов складывается информация об изменении координат обьектов, раз в минуту все данные забираются из буферов и append'ом (оракловый метод вставки большого кол-ва строк) вставляются в основное хранилище, что дает оптимальное построение bitmap индексов.

Таких примеров масса. При выборе архитектуры бд надо изходить из возможностей СУБД и профиля данных, но ни в коем случае не из удобства разработки и написания запросов.
Так вроде-как версионироавние в постгресе работает уже на уровне отдельного поля, а определенные вами проблемы давно уже остались в прошлом?
Не все так просто. Вот цитата из документации Postgres'а 8.3
"UPDATEs and DELETEs leave dead tuples behind, as do failed INSERTs. Previously only VACUUM could reclaim space taken by dead tuples. With HOT dead tuple space can be automatically reclaimed at the time of INSERT or UPDATE if no changes are made to indexed columns. This allows for more consistent performance. Also, HOT avoids adding duplicate index entries. "
И ссылка на разъяснения по-русски: http://citforum.univ.kiev.ua/database/po…

Экономия места лишь на уровне индекса, если не меняется поле по которому построен индекс. Плюс не надо ждать вакууминга для повторного использования места, занимаемого версией строки в которой отпала необходимость. Да к тому же все это верно пока версии строки внутри одной страницы памяти. Так что экономия лишь процентная. Если строки большие (много полей), все это хуже работает. Проблема реально остается.
Да и не так важны детали. Я просто привел пример того как имплементация СУБД может кардинально влиять на схему таблиц и архитектуру приложения в целом.
Правильный, отличный пример :)
В этом смылсе, задача немного не до конца была составлена. Спасибо всеравно.
UFO just landed and posted this here
Всё легко делается через наследование, и должно так делаться. Незнаю уж что вы гоните и запутываете новичков.
Ну подскажите тогда на примере, как это делается правильно, чтобы я перестал путать новичков.
Прочитайте М. Фаулер "Архитектура корпоративных программных приложений". Там это всё разжёвано. Но общая суть такая: хранить общие свойства в одной таблице, а все остальные в специфичных.
Самый существенный пост)
Только Фаулер не отдает предпочтение какому-то конкретному варианту - у всего есть плюсы и минусы.
Я сам предпочитал указанный Вами вариант.
Вообще, с первого взгляда понятно, что задача - распространенная, а значит, наверняка есть хорошие материалы на эту тему. Вся тема называется "объектно-реляционное преобразование", и по ней много и статей, и тех же фреймворков.
Нормализованная БД: (Наши объекты: object_id, name) -< (Наш космос: id, x, y, object_id) -< (Свойства объектов: id, var_id, value) >- (Наши свойства: var_id, name[, type]).

Далее можно перейти к денормализации БД за счет избыточности названий объектов и их свойств : (Наш космос: id, x, y, object_name) -< (Свойства объектов: id, name[, type,] value).

Денормализация за счет использования XML (PostgresSQL хвалят) и JSON:
(Наш космос: id, x, y, object_name, name[, type,] xml_or_json_value).

Нужно учесть функциональность ПО а именно, к каким полям будут выполняться запросы, т.к. в случае с использованием XML или JSON, нужно индексировать большое количество данных и осуществлять медленный поиск (xml_or_json_value LIKE '%значение%').

P.S. Есть множество решений построения архитектуры БД. Для достижения высокой производительности и максимальной зажатости данных, нужно рассматривать впервую очередь поведения самого ПО, на вооружение можно взять примеры UML-диаграмм. И не забываем про постреляционные БД.
UPDATE: Постреляционные СУБД.
UPDATE: Наш космос: id, x, y, object_name) >< (Свойства объектов: id, name[, type,] value)
Вопрос на засыпку.
В методе "Каждый параметр объектов в отдельной таблице" в минусах числится Необходимо несколько запросов для выборки всех нужных объектов
Собственно, что имеется в виду? Ведь при такой структуре и использовании left join для выборки всех достаточно одного запроса, собранного вокруг таблицы space (а то и view можно слабать).

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

И к плюсам данного метода я отнес бы возможность работать со всеми объектами сразу и с конкретным типом при необходимости.

P.S. В таблице space совершенно не обязательно хранить тип, его можно определять по наличию данных в одной из таблиц с параметрами по ID. А это поле только занимает место и по сути нигде не используется.
Я тоже считаю, что это наилучший вариант, но Left Join после explain превращается (для данного примера) в 4 выборки по 4м БД :)

Если вы мне расскажете, как это обойти- тогда уберу этот пункт! Возможно, я просто ошибаюсь.
Понял о чем речь.
Я говорил о том, что можно получить данные в один recordset и обрабатывать в едином цикле, а не составлять несколько запросов. Ну, немного про разные уровни.
Ещё можно комбинировать разные методы: например хранить некоторые поля в json, а те, по которым будет производиться поиск, отдельно. И ещё можно хранить в одном поле разную для разных объектов информацию, например в поле field_1 для объекта spaceship количество пасажиров, а для объекта alien количество тентаклей. Хотя конечно это не очень красивый вариант.
одна таблица, которая хранит сущности, которые имеют свойства: тип, идентификатор внутри типа, имя, описание, дату создания, модификации и тп
одна таблица, которая хранит "космические объекты", которые имеют такие свойства как масса, координаты, ориентация и тп. каждый объект идентифицируется идентификатором+типом
одна таблица, которая хранит "звёзды", которые характеризуются... ну чем-то там характеризуются ;-) идентификатор в этой таблице является уникальным, а поле "тип" всегда равно "звезда" или вообще отсутствует.

итого, информация о кажом объекте распределена по трём таблицам: сущность, космический объект, звезда. связь 1 к 1 по идентификатору и типу.
Под словосочетанием "один-к-разным" Вы подразумеваете наследование.
Посмотрите как, например, наследование реализовано в JPA:
http://www.jpox.org/docs/1_2/jpa_orm/inheritance.html
Два основных принципа: все в одной таблице, либо отдельная таблица на класс.
Я бы использовал либо первый либо последний. Первый вообще позволяет хранить любой объект вдруг понадобиться еще, хотя возникает вопрос, нафига козе боян т.е. SQL может быть поискать что-нить более подходящее.

Потянулся было уже писать свой вариант, но понял что он и есть последний.

а про метод "Параметры объектов в отдельной таблице" ИМХО сложность построения запросов не оправдывает гибкость данной схемы. Я когда то писал вещь используя такую схему честно говоря голову я себе тогда изрядно поломал. но получилось ;) Правда у меня еще разные типы данных в разных таблицах лежали.
А я ничего страшного в этом методе не увидел. Реализовал его вот так:

function load_globals($objid){
  $result = mysql_query('SELECT param_name,value FROM `table` WHERE `object_id`=',$objid));
  while($tmp = mysql_fetch_array($result, MYSQL_ASSOC))
    $global[$tmp['param_name']][]=$tmp['value'];
  mysql_free_result($result);
  return $global;
}
Ну это если выбирать тока по id. А если выбрать по нескольким полям?
Понял. Не подумал.
Я просто этот метод использую лишь для хранения конфигов. :)
Можно выбрать самый первый описанный вариант...
а по полям поиск организовать так

select modelid from xxx where (( propid = 1) and (val=1)) and modelid in (select modelid from xxx where (( propid = 2) and (val=2)))

для случая когда у нас связывающая таблица имеет вид
modelid - айди объекта
propid - айди свойства
val - значение свойства
Sign up to leave a comment.

Articles