Да, идентификация ассоциаций или отношений «многие ко многим» (для краткости M: N) - это ситуация, с которой практикующий специалист по базам данных довольно часто сталкивается при разработке концептуальной схемы. Связи указанных коэффициентов кардинальности возникают в бизнес-средах очень различной природы, и при правильном представлении на логическом уровне посредством, например, соглашения SQL-DDL, они не вносят вредных избыточностей.
Таким образом, цель упражнения по моделированию базы данных должна заключаться в том, чтобы с высокой точностью отражать соответствующие характеристики интересующего бизнес-контекста ; следовательно, если вы правильно определили, что существует множество ассоциаций M: N, вы должны выразить их в (а) концептуальной схеме, а также в (б) соответствующих декларациях логического уровня, независимо от того, сколько соединений этого - или любого другой - вид коэффициентов кардинальности должны быть рассмотрены.
Бизнес правила
Вы поставили хорошо контекстуализированный вопрос, а также пояснили, что база данных, над которой вы работаете, является чисто гипотетической, что является важным моментом, поскольку я считаю, что бизнес-сценарий «реального мира», такой как рассматриваемый, будет гораздо более обширным и, следовательно, предполагает более сложные информационные требования.
Я решил (1) внести несколько модификаций и дополнений в предоставленные вами бизнес-правила, чтобы (2) создать более описательную концептуальную схему - хотя все еще довольно гипотетическую -. Вот некоторые из формулировок, которые я собрал:
- Сторона 1 является либо лицо или организация
- Партия классифицируется точно-один PartyType
- PartyType классифицирует ноль-один-или-многие Стороны
- Организация разрабатывает нулевые один или много- продукты
- Продукт представляет собой либо системы или игры
- Продукт классифицируется точно-один ProductType
- Система каталогизирована ровно-одной SystemType
- Игра может быть воспроизведена через один-ко-многим системам
- Система используется для воспроизведения один-ко-многим Игр
- Игра классифицируется нулевым одной или многим жанрам
- Жанр классифицирует ноль-один-или-много игр
- A Продукт берет свое начало один-ко-многим Джобсом
- Работа выполняется на ноль-один-или-многих людей , , которые играют в роли в Коллабораторов
- Лицо является Collaborator в нуль один или-много рабочих мест
1 Сторона - это термин, используемый в правовом контексте, когда он относится к какому-либо лицу или группе лиц, которые составляют единое целое, поэтому данный термин подходит для обозначения людей и организаций .
Диаграмма IDEF1X
Впоследствии я создал диаграмму IDEF1X 2 , показанную на рисунке 1 (не забудьте нажать на ссылку, чтобы увидеть ее в более высоком разрешении), консолидируя в одном графическом устройстве представленные выше бизнес-правила (наряду с некоторыми другими, которые кажутся актуальными):
2 Определение интеграции для информационного моделирования ( IDEF1X ) - это очень рекомендуемый метод моделирования данных, который был установлен в качестве стандарта в декабре 1993 года Национальным институтом стандартов и технологий США (NIST). Он основан на (а) раннем теоретическом материале, автором которого является единственный создатель реляционной модели, то есть доктор Э. Ф. Кодд; (b) представление данных об отношениях между сущностями, разработанное доктором П.П. Ченом ; а также о (c) методике проектирования логических баз данных, созданной Робертом Г. Брауном.
Как вы можете видеть, я изобразил только три ассоциации M: N с помощью соответствующих типов ассоциативных сущностей , то есть:
- коллаборационист
- SystemGame
- GameGenre
Среди других аспектов, есть две отличные структуры подтипа супертипа , где:
Персона и Организация являются взаимоисключающими подтипами сущностей Стороны , их подтипами
Продукт - это супертип System и Game , которые, в свою очередь, являются взаимоисключающими подтипами.
Если вы не знакомы с ассоциациями супертип-подтип, вы можете найти помощь, например, мои ответы на вопросы, озаглавленные:
Иллюстративная логическая компоновка SQL-DDL
Соответственно, мы должны убедиться, что на логическом уровне:
- Каждый тип сущности представлен отдельной базовой таблицей
- Каждое отдельное свойство применимого типа объекта обозначается конкретным столбцом
- Точный тип данных фиксирован для каждого столбца , чтобы гарантировать, что все содержащиеся в нем значения принадлежат конкретному и четко определенному набору, будь то INT, DATETIME, CHAR и т. Д. (Конечно, при использовании, например, Firebird или PostgreSQL , вы могли бы использовать более мощные ДОМЕНЫ)
- Несколько ограничений настраиваются (декларативно), чтобы гарантировать, что утверждения в виде строк, сохраняемых во всех таблицах, соответствуют бизнес-правилам, определенным на концептуальном уровне
Поэтому я объявил следующую схему DDL на основе ранее показанной диаграммы IDEF1X:
CREATE TABLE PartyType ( -- Stands for an independent entity type.
PartyTypeCode CHAR(1) NOT NULL, -- To retain 'P' or 'O'.
Name CHAR(30) NOT NULL, -- To keep 'Person' or 'Organization'.
--
CONSTRAINT PartyType_PK PRIMARY KEY (PartyTypeCode)
);
CREATE TABLE Party ( -- Represents an entity supertype.
PartyId INT NOT NULL,
PartyTypeCode CHAR(1) NOT NULL, -- To hold the value that indicates the type of the row denoting the complementary subtype occurrence: either 'P' for 'Person' or 'O' for 'Organization'.
CreatedDateTime TIMESTAMP NOT NULL,
--
CONSTRAINT Party_PK PRIMARY KEY (PartyId),
CONSTRAINT PartyToPartyType_FK FOREIGN KEY (PartyTypeCode)
REFERENCES PartyType (PartyTypeCode)
);
CREATE TABLE Person ( -- Denotes an entity subtype.
PersonId INT NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
GenderCode CHAR(3) NOT NULL,
BirthDate DATE NOT NULL,
--
CONSTRAINT Person_PK PRIMARY KEY (PersonId),
CONSTRAINT Person_AK UNIQUE (FirstName, LastName, GenderCode, BirthDate), -- Composite ALTERNATE KEY.
CONSTRAINT PersonToParty_FK FOREIGN KEY (PersonId)
REFERENCES Party (PartyId)
);
CREATE TABLE Organization ( -- Stands for an entity subtype.
OrganizationId INT NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Name CHAR(30) NOT NULL,
FoundingDate DATE NOT NULL,
--
CONSTRAINT Organization_PK PRIMARY KEY (OrganizationId),
CONSTRAINT Organization_AK UNIQUE (Name), -- Single-column ALTERNATE KEY.
CONSTRAINT OrganizationToParty_FK FOREIGN KEY (OrganizationId)
REFERENCES Party (PartyId)
);
CREATE TABLE ProductType ( -- Represents an independent entity type.
ProductTypeCode CHAR(1) NOT NULL, -- To enclose the values 'S' and 'G' in the corresponding rows.
Name CHAR(30) NOT NULL, -- To comprise the values 'System' and 'Person' in the respective rows.
--
CONSTRAINT ProductType_PK PRIMARY KEY (ProductTypeCode)
);
CREATE TABLE Product ( -- Denotes an entity supertype.
OrganizationId INT NOT NULL,
ProductNumber INT NOT NULL,
ProductTypeCode CHAR(1) NOT NULL, -- To keep the value that indicates the type of the row denoting the complementary subtype occurrence: either 'S' for 'System' or 'G' for 'Game'.
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Product_PK PRIMARY KEY (OrganizationId, ProductNumber), -- Composite PRIMARY KEY.
CONSTRAINT ProductToOrganization_FK FOREIGN KEY (OrganizationId)
REFERENCES Organization (OrganizationId),
CONSTRAINT ProductToProductType_FK FOREIGN KEY (ProductTypeCode)
REFERENCES ProductType (ProductTypeCode)
);
CREATE TABLE SystemType ( -- Stands for an independent entity type.
SystemTypeCode CHAR(1) NOT NULL,
Name CHAR(30) NOT NULL,
--
CONSTRAINT SystemType_PK PRIMARY KEY (SystemTypeCode)
);
CREATE TABLE MySystem ( -- Represents a dependent entity type.
OrganizationId INT NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
SystemNumber INT NOT NULL,
SystemTypeCode CHAR(1) NOT NULL,
ParticularColumn CHAR(30) NOT NULL,
--
CONSTRAINT System_PK PRIMARY KEY (OrganizationId, SystemNumber),
CONSTRAINT SystemToProduct_FK FOREIGN KEY (OrganizationId, SystemNumber)
REFERENCES Product (OrganizationId, ProductNumber),
CONSTRAINT SystemToSystemType_FK FOREIGN KEY (SystemTypeCode)
REFERENCES SystemType (SystemTypeCode)
);
CREATE TABLE Game ( -- Denotes an entity subtype.
OrganizationId INT NOT NULL, -- To be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
GameNumber INT NOT NULL,
SpecificColumn CHAR(30) NOT NULL,
--
CONSTRAINT Game_PK PRIMARY KEY (OrganizationId, GameNumber),
CONSTRAINT GameToProduct_FK FOREIGN KEY (OrganizationId, GameNumber)
REFERENCES Product (OrganizationId, ProductNumber)
);
CREATE TABLE Genre ( -- Stands for an independent entity type.
GenreNumber INT NOT NULL,
Name CHAR(30) NOT NULL,
Description CHAR(90) NOT NULL,
--
CONSTRAINT Genre_PK PRIMARY KEY (GenreNumber),
CONSTRAINT Genre_AK1 UNIQUE (Name),
CONSTRAINT Genre_AK2 UNIQUE (Description)
);
CREATE TABLE SystemGame ( -- Represents an associative entity type or M:N association.
SystemOrganizationId INT NOT NULL,
SystemNumber INT NOT NULL,
GameOrganizationId INT NOT NULL,
GameNumber INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT SystemGame_PK PRIMARY KEY (SystemOrganizationId, SystemNumber, GameOrganizationId, GameNumber), -- Composite PRIMARY KEY.
CONSTRAINT SystemGameToSystem_FK FOREIGN KEY (SystemOrganizationId, SystemNumber) -- Multi-column FOREIGN KEY.
REFERENCES MySystem (OrganizationId, SystemNumber),
CONSTRAINT SystemGameToGame_FK FOREIGN KEY (SystemOrganizationId, GameNumber) -- Multi-column FOREIGN KEY.
REFERENCES Game (OrganizationId, GameNumber)
);
CREATE TABLE GameGenre ( -- Denotes an associative entity type or M:N association.
GameOrganizationId INT NOT NULL,
GameNumber INT NOT NULL,
GenreNumber INT NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT GameGenre_PK PRIMARY KEY (GameOrganizationId, GameNumber, GenreNumber), -- Composite PRIMARY KEY.
CONSTRAINT GameGenreToGame_FK FOREIGN KEY (GameOrganizationId, GameNumber)
REFERENCES Game (OrganizationId, GameNumber), -- Multi-column FOREIGN KEY.
CONSTRAINT GameGenreToGenre_FK FOREIGN KEY (GenreNumber)
REFERENCES Genre (GenreNumber)
);
CREATE TABLE Job ( -- Stands for an associative entity type or M:N association.
OrganizationId INT NOT NULL,
ProductNumber INT NOT NULL,
JobNumber INT NOT NULL,
Title CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Job_PK PRIMARY KEY (OrganizationId, ProductNumber, JobNumber), -- Composite PRIMARY KEY.
CONSTRAINT Job_AK UNIQUE (Title), -- Single-column ALTERNATE KEY.
CONSTRAINT JobToProduct_FK FOREIGN KEY (OrganizationId, ProductNumber) -- Multi-column FOREIGN KEY.
REFERENCES Product (OrganizationId, ProductNumber)
);
CREATE TABLE Collaborator ( -- Represents an associative entity type or M:N association.
CollaboratorId INT NOT NULL,
OrganizationId INT NOT NULL,
ProductNumber INT NOT NULL,
JobNumber INT NOT NULL,
AssignedDateTime DATETIME NOT NULL,
--
CONSTRAINT Collaborator_PK PRIMARY KEY (CollaboratorId, OrganizationId, ProductNumber, JobNumber), -- Composite PRIMARY KEY.
CONSTRAINT CollaboratorToPerson_FK FOREIGN KEY (CollaboratorId)
REFERENCES Person (PersonId),
CONSTRAINT CollaboratorToJob_FK FOREIGN KEY (OrganizationId, ProductNumber, JobNumber) -- Multi-column FOREIGN KEY.
REFERENCES Job (OrganizationId, ProductNumber, JobNumber)
);
Уместно подчеркнуть, что существуют объявления составных ограничений PRIMARY KEY для нескольких таблиц, которые обозначают иерархию соединений, которые имеют место между концептуальными типами сущностей, расположение, которое может быть очень полезным в отношении извлечения данных, когда, например, выражается SELECT операции, включающие предложения JOIN для получения производных таблиц.
Да, (i) каждая ассоциация M: N и (ii) каждый из связанных типов объектов обозначается (iii) соответствующей таблицей в логической структуре DDL, поэтому обратите особое внимание на ограничения PRIMARY и FOREIGN KEY (и примечания, которые я оставил в качестве комментариев) таблиц, представляющих эти концептуальные элементы, поскольку они помогают гарантировать, что соединения между соответствующими строками соответствуют применимым коэффициентам кардинальности.
Использование составных ключей было введено д-ром Э.Ф. Коддом с самого начала реляционной парадигмы, что продемонстрировано на примерах, которые он включил в свою основополагающую работу 1970 года, озаглавленную «Реляционная модель для больших совместно используемых банков данных» (которая точно также представляет самый элегантный метод обработки концептуальных ассоциаций M: N).
Я установил db <> fiddle и SQL Fiddle , оба работающие на Microsoft SQL Server 2014, чтобы можно было проверить структуру «в действии».
нормализация
Нормализация - это процедура логического уровня, которая подразумевает, в основном, следующее:
Устранение неатомарных столбцов через первую нормальную форму, так что манипулирование данными и их сжатие намного легче справиться с использованием подъязыка данных (например, SQL).
Избавление от нежелательных зависимостей между столбцами определенной таблицы с помощью последовательных нормальных форм, чтобы избежать аномалий обновления .
Естественно, необходимо учитывать значение, которое несут рассматриваемая таблица (и) и колонка (и).
Мне нравится думать о нормализации как о тесте, основанном на науке, который дизайнер применяет к соответствующим элементам после того, как он определил стабильное расположение на логическом уровне, чтобы определить, соответствуют ли его элементы каждой из нормальных форм или нет. Затем, при необходимости, дизайнер принимает соответствующие корректирующие меры.
избыточность
В реляционной модели, хотя дублирование значений, содержащихся в столбцах, не только допустимо, но и ожидается , повторяющиеся строки запрещены . В этой степени, насколько я вижу, во всех таблицах, включенных в логическую структуру, представленную ранее, предотвращается дублирование строк и другие виды вредных избыточностей, возможно, вы хотели бы прояснить свою озабоченность в этом отношении.
В любом случае, вы, безусловно, можете (а) самостоятельно оценить указанную структуру с помощью обычных форм, чтобы определить, соответствует ли она требованиям, и (б) изменить ее при необходимости.
Связанные ресурсы
- В этой серии публикаций я представлю некоторые соображения по поводу прямой ассоциации M: N, которая может связывать экземпляры двух разных типов сущностей.
- В этом другом я предлагаю подход для обработки вхождения «ведомости материалов» или «взрыва деталей», в котором я описываю, как соединять отдельные экземпляры одного и того же типа сущности.
Троичные ассоциации
Есть еще один важный аспект, который вы затронули в комментариях (опубликованных в удаленном ответе):
Каждый раз, когда я пытаюсь создать мост, у элементов этого моста также есть «многие ко многим», у меня складывается впечатление, что это запрещено или, по крайней мере, не рекомендуется.
Это обстоятельство, похоже, указывает на то, что одна из ваших проблем связана с концептуальными троичными ассоциациями . В сущности, такого рода ассоциации возникают, когда существуют (1) отношения, включающие (2) два других отношения, иными словами «отношения между отношениями» - тоже типичная ситуация, поскольку отношения - это сущность сама по себе -.
Эти механизмы, при надлежащем управлении, также не приносят вредных увольнений. И, да, если есть определенный вариант использования, в котором вы указываете, что такие отношения представляются между типами сущностей «реального мира», вы должны (i) моделировать и (ii) объявлять их с точностью на логическом уровне.
- Вот вопрос и ответ, где мы анализируем область дискурса об исследованиях , которая включает пример троичной ассоциации.
- В этом очень хороший ответ , @Ypercube представляет собой схему и соответствующую структуру DDL для интересной форме алмаза отношений , которая очень похожа на этот класс сценариев.