Архитектура
Да, на самом деле.Вы можете реализовать это, используя унифицированную архитектуру, где каждый объект имеет уникальную запись с уникальным идентификатором в таблице объектов.Это также может быть расширяемым, так что вы можете добавить дополнительные типы объектов в отношение позже, добавив одну новую таблицу для каждого типа объекта.
Любая другая архитектура, кроме той, что я собираюсь описать, потребует экспоненциального роста вколичество таблиц для добавления дополнительных связей ко всем существующим типам объектов.
Таблица 1 : "ObjectTypes" .Эта таблица будет хранить список типов объектов, которые у вас есть.В нем будет три записи с именами «Страница», «Ссылка» и «Область».
- ID int (идентификатор)
- Имя nvarchar (50)
Таблица 2 : "Объекты" .В этой таблице будут храниться экземпляры объектов с уникальными 64-битными идентификаторами, которые идентифицируют тип каждого объекта и дают объекту уникальное удобочитаемое имя или описание.Механизм базы данных обрабатывает уникальность для вас, просто устанавливая поле идентификатора в качестве столбца идентификаторов.Это идеальный вариант, поскольку позволяет легко внедрить любую систему, для которой требуются уникальные идентификаторы объектов, например систему разрешений для управления доступом к определенным страницам, ссылкам, областям и т. Д.
- ID идентификатор bigint
- Тип int (внешний ключ к таблице ObjectTypes.ID)
- Имя nvarchar (200)
Таблица 3 : "ObjectRelationships" .Эта таблица представляет отношения между любыми двумя объектами любого типа.Если бы это была строгая иерархия, где объект мог бы иметь только одного родителя за раз, тогда вы могли бы фактически сделать ее временной иерархией, добавив поле отметки времени и создав представление, которое выбирает самую последнюю запись для каждого объекта.В вашем случае, поскольку у вас есть объекты, которые могут принадлежать нескольким другим объектам одновременно, строгая временная иерархия не требуется;но он также достаточно гибок, чтобы вы могли хранить для некоторых объектов одно отношение на объект и при этом иметь неповрежденную временную иерархию для подмножества типов объектов.Для двунаправленных отношений потребуются две записи, в которых поменялись местами ObjectID и RelatedObjectID.Обратите внимание, что для других архитектур также потребуются две записи, просто в двух отдельных таблицах.В качестве альтернативы, вы можете просто рассматривать одну запись как двунаправленное отношение, но это повлияет на то, как вы пишете и интерпретируете запросы.
- ObjectID bigint (внешний ключ для объектов.Я БЫ);также рекомендовал бы кластеризованный индекс для этого поля
- RelatedObjectID bigint (внешний ключ для Objects.ID);также рекомендовал бы рассматривать это направленное отношение как ParentObjectID или ContainerObjectID, и вы все равно можете добавить обратные отношения для двунаправленных отношений
- [Timestamp] datetime (SQL Server datetime2 (7))
Таблицы с 4 по 2 ^ 32 : "Страницы" , "Ссылки" , "Области" и т. д. и т. д. и т. д. Каждая из этих трех или более таблиц хранит информацию, уникальную для конкретного типа объекта.Замечательная вещь в этой архитектуре заключается в том, что вам нужно добавить только одну таблицу для каждого нового типа объекта (вместе с новой записью в таблице ObjectTypes), потому что всегда есть одна таблица, в которой хранятся ваши объектные отношения.
Запрос данных
Если вы хотите найти все ссылки на странице, вы можете запросить прямые связи между страницами и ссылками, например так:
select L.* from Links L
inner join ObjectRelationships R on R.ObjectID = L.ID
where R.RelatedObjectID = @pageID;
или вы можете найти все ссылкина странице, косвенно связанной с такими областями, как:
--//view LinkRelationships = select * from Links L inner join ObjectRelationships R on R.ObjectID = L.ID
--//view AreaRelationships = select * from Areas A inner join ObjectRelationships R on R.ObjectID = A.ID
select L.* from LinkRelationships L
inner join AreaRelationships A on L.RelatedObjectID = A.ObjectID
inner join Pages P on P.ID = A.RelatedObjectID
where P.ID = @pageID;
Предполагая, что вы храните двунаправленные отношения (требуется две записи для каждого отношения, меняя значения ObjectID и RelateObjectID), вы можете найти все страницы, которые содержат определенную ссылку.Как я уже упоминал, для других архитектур также потребуются две записи, просто в двух отдельных таблицах.
select P.* from Pages P
inner join ObjectRelationships R on R.RelatedObjectID = P.ID
where R.ObjectID = @linkID;
Эта архитектура очень гибкая и очень эффективная с точки зрения дискового пространства (отлично подходит для устранения узких мест дискового ввода-вывода), но это требует от вас удобного выполнения соединений и представления отношений как различных «уровней» в иерархии, что демонстрируется представлениями * Level в примере запроса.Я на самом деле создаю эти представления, чтобы упростить запросы и сделать их более понятными.