Здравствуйте, Donz, Вы писали:
_FR>>Например, есть таблица Заметки { NoteId, Note }, есть таблицы Товары, Служащие, Контракты, Адреса, … всё что угодно. Каждый Товар, Служащий, Контракт или Адрес может иметь несколько Заметок. Всё. Как?
D>А чем тут поможет еще одна таблица с ID заметок? Может сразу ссылаться на записи в таблице заметок?
D>Если количество заметок динамично для каждой сущности, то проще сделать через дополнительную таблицу для связи многие-ко-многим: EntitiesNotes (EntityId, EntityType, NoteId), где EntityType задается через where в селекте. Или чтобы отношения между таблицами не перекладывать на программистов, сделать по одному полю на каждый тип объектов: EntitiesNotes (ProductId, EmployeeId, ..., NoteId). Но тут неудобство в создании первичного ключа — надо вводить суррогатные NULL, плюс пересоздавать ключ при введении дополнительной сущности.
Вот как раз "безымянных" "EntityId, EntityType" избежать и пытаюсь. А так же созданием на каждую таблицу отдельного поля — сиквелу будет казаться, что от нотесов до какой-либо таблицы ведёт более одного пути (например, если между ProductId и EmployeeId есть связь).
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Sinix, Вы писали:
_FR>>А какой поинт в том, что бы дублировать структуру таблицы Notes? S>Те же, что и для любой нормализации. S>Упрощаются запросы, работает быстрее (слегка), больше покрываемых юз-кейсов (например, легко можно выбрать все заметки заказчиков), проще в саппорте, исключаются сценарии дублирования — когда одна заметка навешена нескольким элементам, не смешиваются различные сущности.
По сравнению с чем? С тем вариантом, что у меня сейчас? Да и в принципе, редко замечал, что нормализация приводит к упрощению запросов более быстрой работе чего-то там.
S>Но это — когда данные обрабатываются влияют на БЛ. Если "только для показать" — лучше EAV'ом (псевдокод)- S>+ check на (TableObject_ID, TableColumn_ID, Table_ID).
Нет, "безымянных" решений не хочется. Связи хочу иметь явные, простые и понятные. Не хватало ещё вркопашную следить за ссылочной целостностью.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Нет, "безымянных" решений не хочется. Связи хочу иметь явные, простые и понятные. Не хватало ещё вркопашную следить за ссылочной целостностью.
Вам всё равно придётся следить за тем, чтобы разным сущностям не был назначен один и тот же список заметок. Любое денормализованное решение будет подвержено уязвимостям обновления. Либо смягчать инварианты, либо баш-на-баш по производительности.
Здравствуйте, Sinix, Вы писали:
_FR>>Нет, "безымянных" решений не хочется. Связи хочу иметь явные, простые и понятные. Не хватало ещё вркопашную следить за ссылочной целостностью. S>Вам всё равно придётся следить за тем, чтобы разным сущностям не был назначен один и тот же список заметок. Любое денормализованное решение будет подвержено уязвимостям обновления. Либо смягчать инварианты, либо баш-на-баш по производительности.
Это меня волнует мало, ибо высокоуровневой операции "назначении списка заметок" не будет (списки заметок будут создаваться сразу при создании сужности, а потом будут делаться только изменения таблицы Notes), а защищаться от того, что кто-то руками станет менять таблицы бессмысленно.
Интересно было бы узнать, какие проблемы могут быть из-за того, что у меня, по-сути, связь от родительской таблицы к детям, а не наоборот.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>>>Существуют ли ситуации, когда в реляционной базе данных было бы оправдано (с точки зрения нормального дизайна, а не кривых требований :о)) наличие таблицы только лишь с одним полем: автоинкрементным целым, первичным ключём? V>>Существуют. Например, реализация наследования в базе. V>>Пример: базовый класс: действующее лицо системы (живёт в таблице с одной автоинкрементной колонкой) V>>Дочерние классы: поставщик (таблица "поставщик", ссылается на действующее лицо),потребитель (таблица "потребитель", ссылается на действующее лицо), менеджер (таблица "менеджер ", ссылается на действующее лицо) и так далее. V>>Имея такую структуру, мы можем легко и однотипно обрабатывать все сущности. Например, добавить таблицу "адрес", ссылающуюся на "действующее лицо". V>>Пример не выдуманный, отлично жил в работающей системе.
_FR>ИМХО, для описанного сценария больше подойдёт агрегирование, в котором таблица с одной колонкой будет не нужна.
Чесно говоря, не понял, какое агрегирование имеется в виду. Засунуть все детали в одну колонку?
> OS>В вашем случае, чтобы получить 3ю нормальную форму придется делать так. > > А тот вариант, что уже есть в каком месте нарушает 3НФ? Вариант такой: > >
> CREATE TABLE NotesList (
> NoteListId int PRIMARY KEY,
> )
>
> CREATE TABLE Notes (
> NoteId int PRIMARY KEY,
> NoteListId int REFERENCES NotesList (NoteListId),
> Note text,
> )
>
> CREATE TABLE Addresses (
> AddressID int PRIMARY KEY,
> Street text,
> City text,
> AddressNoteListId int REFERENCES NotesList (NoteListId),
> StreetNoteListId int REFERENCES NotesList (NoteListId),
> CityId int REFERENCES NotesList (NoteListId),
> )
>
Выглядит не плохо... Особенно если тащить более-менее простыми запросами
в объектную модель, а там уже осмысленные вещи делать.
-- запрос списка адресов и кол-ва определенных заметок
-- для вашего вариантаSELECT A.AddressID, count(N.NoteID)
FROM Addresses A
INNER JOIN Notes N
ON N.NoteListID = A.AddressNoteListID
GROUP BY A.AddressID
-- для моего вариантаSELECT A.AddressID, count(NA.NoteID)
FROM Adresses A
INNER JOIN AddressNotes NA
ON NA.AddressID = A.AddressID
GROUP BY A.AddressID
Различия только в том, что условие N.NoteListID = A.AddressNoteListID
напрямую не подпадает под FOREIGN KEY, тогда как NA.AddressID =
A.AddressID — явно по FOREIGH KEY.
Не думаю, что для современных СУБД это представляет сложность... Хотя...
как знать.
У меня тут еще такая идея родилась. Notes не имеют к Address прямого
отношения. Это такой инструмент для работы с адресами в определенно
софте (вашем софте). Никакие операции связанные с адресами не должны
учитывать нотисы. Может быть функционально разделить эти понятия? Ввести
систему управления нотисами — она окажется простой дальше некуда.
CREATE TABLE Note (
NoteID integer PRIMARY KEY,
Note text NOT NULL,
-- дата добавления, автор, коментарий всякое такое.
Target varchar NOT NULL,
-- ex. target. 'mssql:mydb.ru:Database1:Addresses:AddressID:447'
-- или
TargetHost varchar NOT NULL,
TargetDB varchar NOT NULL,
TargetTable varchar ....
);
Эта система каким-то образом будет ссылаться на сущьности (я предлагаю
использовать какой-нибудь URL), а тот софт который будет работать с
адресами будет использовать и систему работы с адресами и систему работы
с нотисами.
Здравствуйте, _FRED_, Вы писали:
D>>А чем тут поможет еще одна таблица с ID заметок? Может сразу ссылаться на записи в таблице заметок?
И все-таки, чем тут поможет еще одна таблица с ID заметок? Может сразу ссылаться на записи в таблице заметок? Этого момента я не понял.
D>>Если количество заметок динамично для каждой сущности, то проще сделать через дополнительную таблицу для связи многие-ко-многим: EntitiesNotes (EntityId, EntityType, NoteId), где EntityType задается через where в селекте. Или чтобы отношения между таблицами не перекладывать на программистов, сделать по одному полю на каждый тип объектов: EntitiesNotes (ProductId, EmployeeId, ..., NoteId). Но тут неудобство в создании первичного ключа — надо вводить суррогатные NULL, плюс пересоздавать ключ при введении дополнительной сущности.
_FR>Вот как раз "безымянных" "EntityId, EntityType" избежать и пытаюсь. А так же созданием на каждую таблицу отдельного поля — сиквелу будет казаться, что от нотесов до какой-либо таблицы ведёт более одного пути (например, если между ProductId и EmployeeId есть связь).
Если надо именнованности, то создай еще одну таблицу EntityTypes (Type, TypeName).
Насчет казаться опять не понял. Есть связь между продуктами и служащими. Если они будут фигурировать в одном запросе, то заметки будут джойниться по вполне определенным полям определенных таблиц. Или речь про апдейт и добавление записей в эту промежуточную таблицу? И в этом случае не совсем понимаю, в чем проблема, кроме суррогатных NULL'ов. Каждое поле ссылается на одну конкретную таблицу и все. Какие там между ними связи — это разве важно?
Здравствуйте, vmpire, Вы писали:
_FR>>ИМХО, для описанного сценария больше подойдёт агрегирование, в котором таблица с одной колонкой будет не нужна. V>Чесно говоря, не понял, какое агрегирование имеется в виду. Засунуть все детали в одну колонку?
Здравствуйте, Donz, Вы писали:
D>>>А чем тут поможет еще одна таблица с ID заметок? Может сразу ссылаться на записи в таблице заметок?
D>И все-таки, чем тут поможет еще одна таблица с ID заметок? Может сразу ссылаться на записи в таблице заметок? Этого момента я не понял.
Я что-то из цитаты вопроса не уловил Можешь из меня процитировать то, что не понятно было?
_FR>>Вот как раз "безымянных" "EntityId, EntityType" избежать и пытаюсь. А так же созданием на каждую таблицу отдельного поля — сиквелу будет казаться, что от нотесов до какой-либо таблицы ведёт более одного пути (например, если между ProductId и EmployeeId есть связь).
D>Если надо именнованности, то создай еще одну таблицу EntityTypes (Type, TypeName).
Это не поможет — я хочу, что целостность заметок поддерживалась самим сервером. В твоём варианте, если я правильно его понимаю, придётся следить за этим самостоятельно.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Интересно было бы узнать, какие проблемы могут быть из-за того, что у меня, по-сути, связь от родительской таблицы к детям, а не наоборот.
Первое пришедшее в голову — большинство ормапперов будет спотыкаться на удалении — если не обернуть всё это непотребство в хранимые процедуры (или view). Нормальный саппорт любого концептуального извращения никогда дешёвым не бывает.
Здравствуйте, _FRED_, Вы писали:
_FR>>>ИМХО, для описанного сценария больше подойдёт агрегирование, в котором таблица с одной колонкой будет не нужна. V>>Чесно говоря, не понял, какое агрегирование имеется в виду. Засунуть все детали в одну колонку? _FR>Нет, агрегирование в другом смысле. _FR>Тогда будет сразу видно, что если в таблице "действующее лицо системы" нет ни одного поля, то такая таблица попросту не нужна.
То есть, просто сделать не связанные таблицы? Я всё-таки пока не понимаю, в чём разница между этими двумя вариантами с точки зрения базы данных.
Здравствуйте, Sinix, Вы писали:
_FR>>Интересно было бы узнать, какие проблемы могут быть из-за того, что у меня, по-сути, связь от родительской таблицы к детям, а не наоборот.
S>Первое пришедшее в голову — большинство ормапперов будет спотыкаться на удалении — если не обернуть всё это непотребство в хранимые процедуры (или view). Нормальный саппорт любого концептуального извращения никогда дешёвым не бывает.
А какие будут сложности и с кем?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, vmpire, Вы писали:
_FR>>>>ИМХО, для описанного сценария больше подойдёт агрегирование, в котором таблица с одной колонкой будет не нужна. V>>>Чесно говоря, не понял, какое агрегирование имеется в виду. Засунуть все детали в одну колонку? _FR>>Нет, агрегирование в другом смысле. _FR>>Тогда будет сразу видно, что если в таблице "действующее лицо системы" нет ни одного поля, то такая таблица попросту не нужна. V>То есть, просто сделать не связанные таблицы? Я всё-таки пока не понимаю, в чём разница между этими двумя вариантами с точки зрения базы данных.
Нет, связь будет. А разница в том, что иметь "пустую базу" может показаться кому-то оправданным, но пустой список аттрибутов — нет.
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>А какие будут сложности и с кем?
Так откуда я знаю, что у вас будет использоваться и как?
Например сценарий "переместить заметку в другой список; удалить сущность и оставшиеся заметки" без допиливания напильником не прожуёт никто. Это если у вас удаление заметок оформлено триггером — чтоб не болталось дохлых. А если не триггером — мы снова вернулись к проблеме ручного управления ссылками.
_FR>Да ёлки-палки, какие могут быть варианты-то? Приведи ради интереса пару других, отличных
_FR>Например, есть таблица Заметки { NoteId, Note }, есть таблицы Товары, Служащие, Контракты, Адреса, … всё что угодно. Каждый Товар, Служащий, Контракт или Адрес может иметь несколько Заметок. Всё. Как?
Notes:
int content_type
varchar object_pk
text note_text
user, date, title, etc
content_type — идентификатор таблицы, на которую ссылается заметка.
Как кодировать content_type и object_pk — дело ваше.
Здравствуйте, Sinix, Вы писали:
_FR>>А какие будут сложности и с кем? S>Так откуда я знаю, что у вас будет использоваться и как?
S>Например сценарий "переместить заметку в другой список; удалить сущность и оставшиеся заметки" без допиливания напильником не прожуёт никто.
Такой задачи перед моими заметками, естественно, никто не ставит. Да и сомвеваюсь, что перед заметками вообще кто-нибудь ставит подобные задачи.
S>Это если у вас удаление заметок оформлено триггером — чтоб не болталось дохлых. А если не триггером — мы снова вернулись к проблеме ручного управления ссылками.
Ничего в БД кроме, собственно, таблиц не будет. Обычный UnitOfWork, в рамках которого даже задача "переместить заметку в другой список; удалить сущность и оставшиеся заметки" (если бы понадобилась) не встанет — если нужно, делается в две транзакции никому не мешая.
Help will always be given at Hogwarts to those who ask for it.
— я вот тут не мог понять, как поможет таблица Attributes. Теперь после еще одного просмотра вроде дошло
_FR>Это не поможет — я хочу, что целостность заметок поддерживалась самим сервером. В твоём варианте, если я правильно его понимаю, придётся следить за этим самостоятельно.
Если взять второй вариант, где на каждую новую сущность заводится еще одно поле, то целостность будет гарантироваться на уровне БД, но с суррогатными ключами.
Но после вкуривания задачи у меня появился еще один вариант. Насколько я понял из вышеуказанного поста, количество групп атрибутов заранее определена для каждой сущности. Если это так, и заводить по одному полю на каждую группу в данном проекте нормально, то более правильный вариант таков:
То есть логичнее содержать в группе атрибутов информацию о том, какие атрибуты в нее входят. При этом управление группами и их содержимым будет намного проще, чем если в каждом атрибуте содержать информацию о том, в какие группы он входит.
Если все-таки хочется содержать информацию о группе атрибута в нем самом, то выходит, что он может быть только в одной группе. А раз так, то зачем нам дублирующая таблица Attributes? Мы ведь можем напрямую связать сущности TableX сразу с атрибутами по идентификатору их группы. То есть будет такой вариант:
Здравствуйте, denger, Вы писали:
_FR>>Да ёлки-палки, какие могут быть варианты-то? Приведи ради интереса пару других, отличных _FR>>Например, есть таблица Заметки { NoteId, Note }, есть таблицы Товары, Служащие, Контракты, Адреса, … всё что угодно. Каждый Товар, Служащий, Контракт или Адрес может иметь несколько Заметок. Всё. Как?
D>Notes:
D> int content_type
D> varchar object_pk
D> text note_text
D> user, date, title, etc
D>content_type — идентификатор таблицы, на которую ссылается заметка. D>Как кодировать content_type и object_pk — дело ваше.
Вы, наверное, не прочитали полностью задачи — никаких неявных связей иметь не хочется. Зачем использовать колдунство, если можно без него? К тому же задача у меня не иметь возможность добавлять заметки ко всему угодно, а уметь возможность добавлять заметки к большому количеству таблиц\полей ("большому" есть больше одного). В предложенном вами сценарии (раз в пятый, наверное, в этом топике) не страивает то, что за ссылочной целостностью необходимо следить самостоятельно. Это не подходит.
D>Так сделано в комментариях джанго — вполне рабочая и достаточно универсальная модель. D>http://code.djangoproject.com/browser/django/trunk/django/contrib/comments/models.py
Да хоть в Northwind-е
Help will always be given at Hogwarts to those who ask for it.