LINUX.ORG.RU

Хранение и объединение записей с разными, но пересекающимися множествами полей

 ,


0

2

Последнее время часто сталкиваюсь такой проблемой.

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

Первое, что приходит в голову - избыточный набор полей для свойств, поле type и NULL во полях, специфичных для типов, к которой запись не принадлежит.

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

Как принято поступать в таком случае?

★★

я бы поступил в разных случаях поразному. если записей много и выбирать быстро, но набор типов сообщений ограничен - 1. Иначе второй.

visual ★★★
()

если набор типов ограничен (и разруливается через наследование) - то реляицонка с доп. таблицей на каждый тип

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

Deleted
()
Ответ на: комментарий от Deleted

если набор типов ограничен (и разруливается через наследование) - то реляицонка с доп. таблицей на каждый тип

А как тогда получить их совместную выборку с сортировкой по общему полю? «разруливается через наследование» - это что-то специальное в реляционной алгебре/конкретных СУБД, или имеется в виду просто наличие общих свойств?

//пока сделал по первому варианту

shatsky ★★
() автор топика
Ответ на: комментарий от shatsky

А как тогда получить их совместную выборку с сортировкой по общему полю?

select * from root_table r 
join concrete_table c on c.id = r.id
order by c.some_field

ась?

«разруливается через наследование» - это что-то специальное в реляционной алгебре/конкретных СУБД, или имеется в виду просто наличие общих свойств?

ты что спросил то? в практике работы с субд ест ькостыль - «наследование» кое-где, например в постгресе он автоматизирован (там есть именно наследование: http://postgresql.ru.net/manual/ddl-inherit.html ) а кое где надо делать ручками - ничего сложного

Deleted
()
Ответ на: комментарий от Deleted

Спасибо.

ась?

Прочитал «доп.» как «отд.»

shatsky ★★
() автор топика
Ответ на: комментарий от RedPossum

серьезно, иначе у тебя будет table base_object(key, common_attr1, common_attr2,..), table special_object1(key(references baseobject.key), attr1, attr2)...

а в монге получится компактно и как надо, если это единственная задача

RedPossum ★★★★★
()

Можешь сделать «по-простому»,по первому варианту, забив на нормализацию и храня всё в одной таблице. К плюсам можно отнести потенциально большую скорость работы.

Можешь сделать «реляционно», по второму варианту. Следить там ни за чем не нужно, всё будет работать автоматом.

create table event(
id int not null,
PRIMARY KEY (id) 
);

create table event_warning(
id int not null,
event_id int not null,
PRIMARY KEY (id) ,
FOREIGN KEY(event_id) REFERENCES event(id) ON DELETE CASCADE
);
От случаев, когда добавлена новая строка в event, но возникла ошибка при добавлении в event_% защищается транзакциями. Описанного недостатка «надо следить» здесь нет и в помине. Следить будет сама СУБД. Есть другой недостаток, снижение общего быстродействия за счёт того, что операции будут производиться не с одной таблицей, а с несколькими.

dmitryalexeeff
()
Последнее исправление: dmitryalexeeff (всего исправлений: 2)


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

Вот где у тебя ошибка в умопостроениях.
Внешний ключ создаётся в «подчинённой» таблице, а не в «главной».
http://hostingkartinok.com/show-image.php?id=1599a7d17e2f33d3f5d71e7d0157250c

dmitryalexeeff
()
Последнее исправление: dmitryalexeeff (всего исправлений: 1)

Можно всю специфику засунуть в одно поле. Например, сериализовать в JSON и записать в BLOB.

Если же используется Postgres, то можно засунуть в hstore — даже индексы будут.

k_andy ★★★
()
Ответ на: комментарий от shatsky

А как тогда получить их совместную выборку с сортировкой по общему >полю?

Если типы - «наследники» известны, то это union.

den73 ★★★★★
()
Ответ на: комментарий от den73

например, есть таблица parent (Общие поля), child1 с полем f1 и child2 с полем f2.

select parent1.*, child1.f1 as child1_f1, null as child2_f2
from parent as parent1 inner join child1 on (условие связи с child1)
union all
select parent2.*, null as child1_f1, child2.f2 as child2_f2
from parent as parent2 inner join child2 on (условие связи с child2)

Если к какой-то записи parent привязаны и child1, и child2 - будет две строчки в наборе данных.

Если к какой-то записи parent нет наследника, то она не попадёт в запрос. Тогда можно так:

select parent1.*, child1.f1 as child1_f1, null as child2_f2
from parent as parent1 left join child1 on (условие связи с child1)
union 
select parent2.*, null as child1_f1, child2.f2 as child2_f2
from parent as parent2 left join child2 on (условие связи с child2)
Отдельная песня - это какой будет у такого запроса план и насколько он будет быстр. Может оказаться заметно медленнее первого.

У меня сейчас в аналогичной ситуации смесь подходов 1 и 2. Часть полей избыточны в parent. При этом часть полей parent имеют переменный смысл, зависящий от типа записи. А для типов, у которых специфичных полей много, ввожу child-ы.

den73 ★★★★★
()
Ответ на: комментарий от dmitryalexeeff

Внешний ключ создаётся в «подчинённой» таблице, а не в «главной».

Да. Хотя можно всё же и наоборот. Просто тогда придётся иметь столько полей в главной, сколько подтипов, и появится изрядное количество рыхловатых индексов.

den73 ★★★★★
()
Ответ на: комментарий от den73

Это будет уже что угодно, только не «FOREIGN KEY».А учитывая что при таком подходе в столбцах, призванных исполнять роль «внешнего ключа» будут нуллы, мы получаем во всей красе нарушение ссылочной целостности. Что такое «рыхловатый индекс»? Что такое «подтип» в СУБД? Не понимаю.

dmitryalexeeff
()
Ответ на: комментарий от dmitryalexeeff

Не хочу вдаваться в терминологические споры. Для меня foreign key - это то, что мой сервер БД считает foreign key. В нём могут быть и null-ы. Подтип - не в СУБД, а в контексте предметной области. Событие вообще - это тип, а частный вид события - это подтип.

den73 ★★★★★
()
Ответ на: комментарий от dmitryalexeeff

рыхловатый индекс - индекс, в котором много null-ов, отчего работа по нему может оказаться неоптимальной, либо оптимизатор будет редко его выбирать из-за плохой статистики, либо ещё что-нибудь подобное возникнет.

den73 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.