Не знаю, как преобразовать переменную сущность в реляционную таблицу

9

ВВЕДЕНИЕ И СООТВЕТСТВУЮЩАЯ ИНФОРМАЦИЯ:

Следующий пример иллюстрирует проблему, с которой я сталкиваюсь:

Животное имеет расу, которая может быть кошкой или собакой . Кошка может быть или сиамской или персидской . Собака может быть немецкой овчаркой или лабрадором-ретривером .

Животное - сильная сущность, в то время как его раса - это атрибут, который может иметь одно из двух предложенных значений (кошка или собака). Оба эти значения являются сложными (я добавил здесь только тип собаки / кошки, чтобы проиллюстрировать проблему, но также может быть имя кошки / собаки и куча других вещей).

ПРОБЛЕМА:

Я не знаю, как создать реляционные таблицы для этого примера.

МОИ УСИЛИЯ ДЛЯ РЕШЕНИЯ ПРОБЛЕМЫ:

Я попытался нарисовать диаграмму ER, используя обозначение Чена, которая представляет проблему, но, будучи новичком, я не знаю, правильно ли я это сделал. Вот что я получил:

введите описание изображения здесь

Я прошу прощения, если я нарисовал что-то не так, пожалуйста, поправьте меня, если это так. Я не хочу просто получить «бесплатное решение», но также научиться решать эту проблему, чтобы я мог решить ее самостоятельно в будущем.

Единственное, что мне приходит в голову, - это создать две отдельные таблицы: одну для кошек и одну для собак. Кроме того, атрибут гонки в таблице Animal будет хранить только значение кошки или собаки . Что-то вроде этого:

Animal< # Animal_ID, race, other attributes >
Cat < # Cat_ID, $ Animal_ID, breed >
Dog < # Dog_ID, $ Animal_ID, breed >

У меня действительно плохое предчувствие по поводу моего решения, и я боюсь, что оно неверное, поэтому вопрос ниже.

ВОПРОСОВ:

  • Как я могу преобразовать мой пример в диаграмму ER?
  • Как преобразовать эту диаграмму ER в реляционные таблицы?

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

Спасибо.

AlwaysLearningNewStuff
источник
1
Преобразование диаграмм EER в таблицы можно найти в этой статье от 1986 г. TJTeorey, D.Yang, JPFry: Методология логического проектирования для реляционных баз данных с использованием расширенной модели взаимосвязи сущностей . Это простая и одна из моих любимых статей.
miracle173

Ответы:

11

Подходящей структурой для этого сценария является модель подкласса / наследования, и она почти идентична концепции, предложенной мною в этом ответе: гетерогенный упорядоченный список значений .

Модель, предложенная в этом вопросе, на самом деле довольно близка в том, что Animalсущность содержит тип (то есть race) и свойства, которые являются общими для всех типов. Однако есть два небольших изменения, которые необходимы:

  1. Удалите поля Cat_ID и Dog_ID из их соответствующих объектов:

    Ключевым понятием здесь является то , что все это Animal, независимо от того race: Cat, Dog, Elephant, и так далее. Учитывая , что отправной точкой, какого - либо конкретного raceиз Animalне действительно нужен отдельный идентификатор с:

    1. Animal_IDуникален
    2. то Cat, Dogи любые дополнительные raceобъекты , добавленные в будущем не сами по себе, в полной мере представляют собой какой - либо конкретной Animal; они имеют значение только при использовании в сочетании с информацией, содержащейся в родительской сущности Animal.

    Следовательно, Animal_IDсвойство в Cat, Dogи т.д. сущности является как PK и FK обратно к Animalлицу.

  2. Различают типы breed:

    То, что два свойства имеют одно и то же имя, не обязательно означает, что эти свойства одинаковы, даже если одно и то же имя подразумевает такие отношения. В этом случае то, что у вас есть на самом деле, на самом деле CatBreedи DogBreedкак отдельные «типы»

Начальные заметки

  1. SQL является специфическим для Microsoft SQL Server (то есть T-SQL). То есть, будьте осторожны с типами данных, так как они не одинаковы во всех RDBMS. Например, я использую, VARCHARно если вам нужно хранить что-либо за пределами стандартного набора ASCII, вы должны действительно использовать NVARCHAR.
  2. Идентификационные поля таблиц «типа» ( Race, CatBreedи DogBreed) являются не автоматическим приращением (т.е. Идентичности в терминах T-SQL) , потому что они являются константами приложения (т.е. они являются частью приложения) , которые являются статическими значениями столбца просмотра в элементе базы данных и представлены как enums в C # (или других языках). Если значения добавляются, они добавляются в контролируемых ситуациях. Я резервирую использование полей автоинкремента для пользовательских данных, которые поступают через приложение.
  3. Соглашение об именах, которое я использую, состоит в том, чтобы называть каждую таблицу подкласса, начиная с имени основного класса, за которым следует имя подкласса. Это помогает упорядочить таблицы, а также четко показывает (не глядя на FK) отношение таблицы подкласса к основной таблице сущностей.
  4. Пожалуйста, ознакомьтесь с разделом «Окончательное редактирование» в конце для заметки о просмотрах.

«Порода» как «раса» - специфический подход

Диаграмма породы как породы
Этот первый набор таблиц - это таблицы поиска / типов:

CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE CatBreed
(
  CatBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  CatBreedAttribute1 INT,
  CatBreedAttribute2 VARCHAR(10)
  -- other "CatBreed"-specific properties as needed
);

CREATE TABLE DogBreed
(
  DogBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  DogBreedAttribute1 TINYINT
  -- other "DogBreed"-specific properties as needed
);

Этот второй листинг является основным объектом «Animal»:

CREATE TABLE Animal
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  Name VARCHAR(50)
  -- other "Animal" properties that are shared across "Race" types
);

ALTER TABLE Animal
  ADD CONSTRAINT [FK_Animal_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

Этот третий набор таблиц является дополнительными объектами подкласса, которые завершают определение каждого Raceиз Animal:

CREATE TABLE AnimalCat
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  CatBreedID INT NOT NULL, -- FK to CatBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Cat"-specific properties as needed
);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_CatBreed]
  FOREIGN KEY (CatBreedID)
  REFERENCES CatBreed (CatBreedID);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);


CREATE TABLE AnimalDog
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  DogBreedID INT NOT NULL, -- FK to DogBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Dog"-specific properties as needed
);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_DogBreed]
  FOREIGN KEY (DogBreedID)
  REFERENCES DogBreed (DogBreedID);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);

Модель, использующая общий breedтип, показана после раздела «Дополнительные примечания».

Дополнительные замечания

  1. Концепция, breedкажется, является центром путаницы. Jcolebrand (в комментарии к вопросу) предположил, что breedэто свойство является общим для разных races, и два других ответа интегрированы как таковые в их модели. Это ошибка, однако, потому что значения для breedне разделяются между различными значениями race. Да, я знаю, что две другие предложенные модели пытаются решить эту проблему, сделав raceодного из родителей breed. Хотя это технически решает проблему взаимоотношений, это не помогает решить общий вопрос моделирования о том, что делать с необычными свойствами, и как обрабатывать объект race, у которого нет breed. Но, в случае, если такая собственность гарантированно существует во всехAnimals, я включу опцию для этого (ниже).
  2. Модели, предложенные Виджайпом и Дэвидом (которые кажутся идентичными), не работают, потому что:
    1. Они либо
      1. не допускайте хранения необычных свойств (по крайней мере, для отдельных экземпляров Animal), или
      2. требуют, чтобы все свойства для всех races были сохранены в Animalобъекте, который является очень плоским (и почти нереляционным) способом представления этих данных. Да, люди делают это постоянно, но это означает наличие множества пустых полей в строке для свойств, которые не предназначены для этого конкретного raceИ, зная, какие поля в строке связаны с конкретным raceобъектом этой записи.
    2. Они не позволяют добавления raceиз Animalв будущем , которое не имеет breedв собственности. И даже если у ВСЕХ Animalесть breed, это не изменит структуру из-за того, о чем ранее было отмечено breed: это breedзависит от race(то есть breedfor Cat- это не то же самое, что и breedдля Dog).

«Порода» как подход к общей / совместной собственности

введите описание изображения здесь
Пожалуйста, обратите внимание:

  1. SQL ниже может быть запущен в той же базе данных, что и модель, представленная выше:

    1. RaceТаблицы одно и то же
    2. BreedСтол новый
    3. Три Animalтаблицы были добавлены с2
  2. Даже Breedбудучи общепринятым в настоящее время свойством, кажется неправильным не Raceуказывать в основной / родительской сущности (даже если это технически корректно с точки зрения отношений). Итак, оба RaceIDи BreedIDпредставлены в Animal2. Чтобы предотвратить несоответствие между RaceIDотмеченными в Animal2и a BreedIDдля другого RaceID, я добавил FK для обоих, RaceID, BreedIDкоторый ссылается на УНИКАЛЬНОЕ ОГРАНИЧЕНИЕ этих полей в Breedтаблице. Я обычно презираю указание ФК на УНИКАЛЬНОЕ ОГРАНИЧЕНИЕ, но вот одна из немногих веских причин для этого. УНИКАЛЬНОЕ ОГРАНИЧЕНИЕ логически является «альтернативным ключом», что делает его действительным для этого использования. Также обратите внимание, что на Breedстоле по-прежнему есть ПК BreedID.
    1. Причина, по которой нельзя использовать только PK в объединенных полях и не использовать UNIQUE CONSTRAINT, состоит в том, что это позволяет BreedIDповторять одно и то же для разных значений RaceID.
    2. Причина того, что PK и UNIQUE CONSTRAINT не переключаются, заключается в том, что это может быть не единственным использованием BreedID, поэтому все еще должна быть возможность ссылаться на конкретное значение, Breedне имея RaceIDдоступного.
  3. Хотя следующая модель работает, у нее есть два потенциальных недостатка в отношении концепции общего доступа Breed(и именно поэтому я предпочитаю таблицы, Raceспецифичные для конкретного пользователя Breed).
    1. Существует неявное предположение, что ВСЕ значения Breedимеют одинаковые свойства. В этой модели нет простого способа иметь разнородные свойства между Dog«породами» и Elephant«породами». Тем не менее, есть еще способ сделать это, что указано в разделе «Окончательное редактирование».
    2. Нет никакого способа разделить Breedболее чем одну расу. Я не уверен, что это желательно делать (или, возможно, не в концепции животных, но, возможно, в других ситуациях, которые будут использовать этот тип модели), но здесь это невозможно.
CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY,
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE Breed
(
  BreedID INT NOT NULL PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  BreedName VARCHAR(50)
);

ALTER TABLE Breed
  ADD CONSTRAINT [UQ_Breed]
  UNIQUE (RaceID, BreedID);

ALTER TABLE Breed
  ADD CONSTRAINT [FK_Breed_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

CREATE TABLE Animal2
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race, FK to Breed
  BreedID INT NOT NULL, -- FK to Breed
  Name VARCHAR(50)
  -- other properties common to all "Animal" types
);

ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Breed]
  FOREIGN KEY (RaceID, BreedID)
  REFERENCES Breed (RaceID, BreedID);


CREATE TABLE AnimalCat2
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalCat2
  ADD CONSTRAINT [FK_AnimalCat2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);

CREATE TABLE AnimalDog2
(
  AnimalID INT NOT NULL PRIMARY KEY,
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalDog2
  ADD CONSTRAINT [FK_AnimalDog2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);


Окончательное редактирование (надеюсь ;-)

  1. Что касается возможности (а затем сложности) обработки разнородных свойств между типами Breed, то есть можно использовать один и то же подкласс / понятие наследования , но с Breedкак основным субъектом. В этой настройке Breedтаблица будет иметь свойства, общие для всех типов Breed(как и Animalтаблица), и RaceIDбудет представлять тип Breed(такой же, как в Animalтаблице). Тогда вы бы подкласс таблицы , такие как BreedCat, BreedDogи так далее. Для небольших проектов это может считаться «чрезмерным проектированием», но это упоминается как вариант для ситуаций, которые выиграют от этого.
  2. Для обоих подходов иногда помогает создавать представления как ярлыки для полных сущностей. Например, рассмотрим:

    CREATE VIEW Cats AS
       SELECT  an.AnimalID,
               an.RaceID,
               an.Name,
               -- other "Animal" properties that are shared across "Race" types
               cat.CatBreedID,
               cat.HairColor
               -- other "Cat"-specific properties as needed
       FROM    Animal an
       INNER JOIN  AnimalCat cat
               ON  cat.AnimalID = an.AnimalID
       -- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
    
  3. Хотя они не являются частью логических сущностей, довольно часто в таблицах есть поля аудита, чтобы по крайней мере получить представление о том, когда записи вставляются и обновляются. Итак, в практическом плане:
    1. CreatedDateПоле должно быть добавлено к Animalтаблице. Это поле не требуется ни в одной из таблиц подкласса (например AnimalCat), поскольку строки, вставляемые для обеих таблиц, должны выполняться одновременно в транзакции.
    2. LastModifiedDateПоле будет добавлено в Animalтаблицу и все таблицы подкласса. Это поле обновляется только в том случае, если обновляется эта конкретная таблица: если обновление происходит в конкретном случае , AnimalCatно не в Animalнем AnimalID, то будет установлено только LastModifiedDateполе в AnimalCat.
Соломон Руцкий
источник
2
Почему-то у меня такое чувство, что ты точно понял, в чем моя проблема. Я посмотрю ваш связанный ответ и внимательно его изучу. Также было бы просто простое определение таблиц (если SQL-запросов слишком много для вас, чтобы писать в данный момент). Если вы решите обновить свой пост SQL-запросами или определениями таблиц, пожалуйста, оставьте мне комментарий. Еще раз спасибо С наилучшими пожеланиями.
AlwaysLearningNewStuff
1
Я пытаюсь применить ваш ответ в моем случае из реальной жизни. Если я буду слепо следовать вашим инструкциям, я считаю, что я мог бы упустить возможность дальнейшей оптимизации моего дизайна. Я хотел бы, чтобы вы взглянули на мой последний вопрос, поскольку вы смогли прекрасно понять мои вопросы и дать отличные ответы. Я сформулировал вопрос об использовании общей модели данных, чтобы быть полезным для будущих читателей. Если у вас возникли проблемы с поиском, оставьте мне комментарий. Спасибо и извините за беспокойство ...
AlwaysLearningNewStuff
@AlwaysLearningNewStuff Привет. Получил это сообщение раньше, но не успел сразу к нему добраться. Я смог найти новый Вопрос, нажав на ваше имя выше, и он показывает все ваши Вопросы :-).
Соломон Руцки
Я имел в виду этот вопрос . В двух словах: у меня есть 3 сущности с общим атрибутом D, поэтому я хотел применить метод из вашего ответа. Два объекта имеют общий атрибут, Eкоторого нет в третьем объекте. Должен ли я игнорировать этот факт и применять стандартное решение, или есть способ дальнейшей оптимизации моего дизайна?
AlwaysLearningNewStuff
4

Во-первых, у вас хорошо получается различать ER-моделирование и реляционное моделирование. Многие новички не делают.

Вот несколько модных слов, которые вы можете использовать для поиска полезных статей в Интернете.

Ваш случай - классический случай класса / подкласса или, если хотите, тип / подтип.

Фраза, которая используется в моделировании ER, является «обобщением / специализацией». И многие статьи показывают это в рамках модели, называемой EER (Enhanced Entity-Relationship). Этого не было в первоначальной презентации Питера Чена по моделированию ER. Это было добавлено позже. Для довольно хорошего резюме поколения / спецификации в формате PDF, нажмите здесь

Затем, при преобразовании случая класса / подкласса в реляционное моделирование вы разрабатываете таблицы. Существует более одного подхода. Два основных подхода называются наследованием отдельных таблиц и наследованием таблиц классов. У каждого есть свои преимущества и недостатки. Лучшая презентация этих двух проектов - от Мартина Фаулера. Вы можете увидеть его схему здесь и здесь .

Большим преимуществом наследования отдельных таблиц является простота. Все это хранится в одной таблице. Большой недостаток - много NULL. Это может тратить пространство и время и привести к запутанной логике.

Наследование таблиц классов требует соединений, но они просты и быстры. Особенно, если вы используете метод, называемый общим первичным ключом, в котором PK в таблицах подклассов является копией PK в таблице суперкласса. Вы можете создавать представления для каждого подкласса, которые объединяют данные суперкласса с данными подкласса.

Наконец, в этой области есть тег, который собирает вопросы, подобные вашему.
Вот оно:

Уолтер Митти
источник
1
+1 Меня смущает отсутствие первичных ключей на диаграммах таблиц. Особенно в "classTableInheritance" я не вижу, что все эти таблицы связаны одним и тем же первичным ключом.
чудо173
@ чудо173 верная точка. По какой-то причине Фаулер не включает в диаграмму PK и FK. Есть другие статьи под наследованием таблиц классов, которые предоставляют эту деталь. Не все реализации наследования таблиц классов объединяют его с общим первичным ключом. Я рекомендую это. Это немного больше работы во время вставки, но проще и быстрее во время совместного поиска.
Уолтер Митти,
3

Я вижу на возможном дизайне как

Таблица Race

RaceId- PK- Int
RaceName - Varchar(50)

Таблица Breed

BreedId - PK- Int
RaceId - FK - Int
BreedName - varchar(50)

Таблица Animal

AnimalId - PK- Int
BreedId - FK - Int
Other Columns....

Эти PK выше будут автоматически увеличивать столбец. Другие столбцы в Animalтаблице могут быть названы соответственно.

введите описание изображения здесь

vijayp
источник
Кроме того, я бы добавил поля с ключами Race и Type (могут быть триггерами) в таблицу Animal, чтобы упростить последующие индексы для повышения скорости.
Фелипе Алькасибар,
0

Ваш нынешний метод неплох. Однако, если вы собираетесь добавить больше рас (птиц, рыб и т. Д.), То создание отдельной таблицы для каждой из них может быть затруднительным. Я бы порекомендовал что-то вроде следующего:

Animal < # Animal_ID, Breed_ID, other attributes >
Breed < # Breed_ID, Race_ID >
Race < # Race_ID >

Порода, на мой взгляд, должна иметь только одну расу. Поэтому, если вы сохраните породу в таблице Animal, вы сможете определить расу, присоединившись к таблице Breed. Очевидно, добавьте любые другие атрибуты (имя, описание и т. Д.) В таблицы породы и расы по мере необходимости.

DavidN
источник