Вопрос

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

Вы можете создать три отдельные таблицы:SpeedboatTests, CarTests и GokartTests.Но многие столбцы в каждой таблице будут одинаковыми (например, идентификатор сотрудника, проводившего тест, направление столкновения (спереди, сбоку, сзади) и т. д.).Однако многие столбцы будут разными, поэтому не стоит просто помещать все тестовые данные в одну таблицу, потому что у вас будет довольно много столбцов, которые всегда будут иметь значение NULL для скоростных катеров, и немало столбцов, которые всегда будут иметь значение NULL. быть нулевым для автомобилей, и немало таких, которые всегда будут нулевыми для картинга.

Допустим, вы также хотите сохранить некоторую информацию, не связанную напрямую с тестами (например, идентификатор сотрудника разработчика тестируемого объекта).Кажется, что эти столбцы вообще нецелесообразно помещать в таблицу «Тесты», особенно потому, что они будут повторяться для всех тестов на одном и том же автомобиле.

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

Speedboats
id | col_about_speedboats_but_not_tests1 | col_about_speedboats_but_not_tests2

Cars
id | col_about_cars_but_not_tests1 | col_about_cars_but_not_tests2

Gokarts
id | col_about_gokarts_but_not_tests1 | col_about_gokarts_but_not_tests2

Tests
id | type | id_in_type | col_about_all_tests1 | col_about_all_tests2
(id_in_type will refer to the id column of one of the next three tables,
depending on the value of type)

SpeedboatTests
id | speedboat_id | col_about_speedboat_tests1 | col_about_speedboat_tests2

CarTests
id | car_id | col_about_car_tests1 | col_about_car_tests2

GokartTests
id | gokart_id | col_about_gokart_tests1 | col_about_gokart_tests2

Что хорошего/плохого в этой структуре и какой способ реализации чего-то подобного будет предпочтительнее?

Что, если есть информация, применимая ко всем транспортным средствам, которые вы предпочитаете иметь в таблице «Транспортные средства»?Будет ли тогда таблица CarTests выглядеть примерно так...

id | vehicle_id | ...

With a Vehicles table like this:
id | type | id_in_type
(with id_in_type pointing to the id of either a speedboat, car, or go-kart)

Кажется, это просто королевский беспорядок.Как ДОЛЖНО быть настроено что-то подобное?

Это было полезно?

Решение

А type и id_in_type дизайн называется Полиморфные ассоциации.Такая конструкция нарушает правила нормализации во многих отношениях.По крайней мере, это должен быть красный флаг, что вы не мочь объявить реальное ограничение внешнего ключа, поскольку id_in_type может ссылаться на любую из нескольких таблиц.

Вот лучший способ определения ваших таблиц:

  • Сделайте абстрактную таблицу Vehicles обеспечить абстрактную точку отсчета для всех подтипов транспортных средств и испытаний транспортных средств.
  • Каждый подтип транспортного средства имеет первичный ключ, который не увеличивается автоматически, а ссылается на Vehicles.
  • Каждый подтип теста имеет первичный ключ, который не увеличивается автоматически, а вместо этого ссылается на Tests.
  • Каждый подтип теста также имеет внешний ключ к соответствующему подтипу транспортного средства.

Вот пример DDL:

CREATE TABLE Vehicles (
 vehicle_id INT AUTO_INCREMENT PRIMARY KEY
);

CREATE TABLE Speedboats (
 vehicle_id INT PRIMARY KEY,
 col_about_speedboats_but_not_tests1 INT,
 col_about_speedboats_but_not_tests2 INT,
 FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id)
);

CREATE TABLE Cars (
 vehicle_id INT PRIMARY KEY,
 col_about_cars_but_not_tests1 INT,
 col_about_cars_but_not_tests2 INT,
 FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id)
);

CREATE TABLE Gokarts (
 vehicle_id INT PRIMARY KEY,
 col_about_gokarts_but_not_tests1 INT,
 col_about_gokarts_but_not_tests2 INT,
 FOREIGN KEY(vehicle_id) REFERENCES Vehicles(vehicle_id)
);

CREATE TABLE Tests (
 test_id INT AUTO_INCREMENT PRIMARY KEY,
 col_about_all_tests1 INT,
 col_about_all_tests2 INT
);

CREATE TABLE SpeedboatTests (
 test_id INT PRIMARY KEY,
 vehicle_id INT NOT NULL,
 col_about_speedboat_tests1 INT,
 col_about_speedboat_tests2 INT,
 FOREIGN KEY(test_id) REFERENCES Tests(test_id),
 FOREIGN KEY(vehicle_id) REFERENCES Speedboats(vehicle_id)
);

CREATE TABLE CarTests (
 test_id INT PRIMARY KEY,
 vehicle_id INT NOT NULL,
 col_about_car_tests1 INT,
 col_about_car_tests2 INT,
 FOREIGN KEY(test_id) REFERENCES Tests(test_id),
 FOREIGN KEY(vehicle_id) REFERENCES Cars(vehicle_id)
);

CREATE TABLE GokartTests (
 test_id INT PRIMARY KEY,
 vehicle_id INT NOT NULL,
 col_about_gokart_tests1 INT,
 col_about_gokart_tests2 INT,
 FOREIGN KEY(test_id) REFERENCES Tests(test_id),
 FOREIGN KEY(vehicle_id) REFERENCES Gokarts(vehicle_id)
);

Альтернативно вы можете объявить Tests.vehicle_id какие ссылки Vehicles.vehicle_id и избавиться от внешних ключей Vehicle_id в каждой таблице подтипов тестов, но это допустит аномалии, такие как тест на скоростном катере, который ссылается на идентификатор картинга.

Другие советы

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

http://martinfowler.com/eaaCatalog/singleTableInheritance.html

http://martinfowler.com/eaaCatalog/classTableInheritance.html

http://martinfowler.com/eaaCatalog/concreteTableInheritance.html

Если количество дополнительных полей/столбцов для подклассов невелико, то с наследованием одной таблицы обычно проще всего справиться.

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

http://www.postgresql.org/docs/8.3/static/ddl-inherit.html

Я бы разбил его на разные таблицы, например.Транспортное средство (идентификатор, тип и т. д.) VehicleAttributes ()VehicleID, AttributeID, Value), CrashTestInfo(VehicleID, CrashtestID, Date и т. д.) CrashtestAttributes(CrashTestID, AttributeID, Value)

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

Если вы используете SQLАлхимия, объектно-реляционный преобразователь для Python, вы можете настроить способ сопоставления иерархий наследования с таблицами базы данных.Объектно-реляционные преобразователи хороши для укрощения утомительного SQL.

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

Выполните поиск в Google по запросу «реляционное моделирование gen-spec».Вы найдете статьи о том, как настроить таблицы, хранящие атрибуты обобщенной сущности (то, что объектно-ориентированные программисты могут назвать суперклассом), отдельные таблицы для каждой специализированной сущности (подкласса) и как использовать внешние ключи для ее связи. все вместе.

Лучшие статьи, IMO, обсуждают спецификации поколения с точки зрения ER-моделирования.Если вы знаете, как преобразовать модель ER в реляционную модель, а затем в таблицы SQL, вы поймете, что делать, когда вам покажут, как моделировать gen-spec в ER.

Если вы просто погуглите «gen-spec», большая часть того, что вы увидите, будет объектно-ориентированной, а не реляционной.Этот материал также может быть полезен, если вы знаете, как преодолеть несоответствие реляционного импеданса объекта.

Ваш дизайн разумен и соответствует правильным правилам нормализации.Возможно, вам не хватает таблицы «Транспортное средство» с идентификатором и типом транспортного средства (т. е. «родительским» для скоростных катеров, автомобилей и картингов...где вы будете хранить такие вещи, как «DesignedByUserId»).Между таблицей «Транспортные средства» и таблицей «Скоростные катера» существует связь «один к одному», а между «Транспортное средство» и «Скоростной катер/Автомобили/Карты» существует связь «1 и только 1» (т.транспортное средство может иметь только 1 рекорд для скоростного катера, автомобиля или картинга)...хотя большинство БД не предлагают для этого простого механизма обеспечения соблюдения.

Одно правило нормализации, которое помогает идентифицировать подобные вещи, заключается в том, что поле должно зависеть только от первичного ключа таблицы.В сводной таблице, где результаты испытаний катеров, автомобилей и картингов хранятся вместе, поля, относящиеся к автомобилям, зависят не только от даты испытания, но также от идентификатора транспортного средства и типа транспортного средства.Первичным ключом таблицы результатов теста является дата теста + идентификатор транспортного средства, а тип транспортного средства не является тем, что делает строку данных теста уникальной (т.можно ли вообще провести испытание 01.01.2009 в 12:30 на одном конкретном транспортном средстве, которое одновременно является катером и автомобилем...неа...сделать невозможно).

Я не особенно хорошо объясняю правило нормализации...но правила 3/4/5 нормальных форм всегда смущают меня, когда я читаю формальные описания.Один из них (3-й/4-й/5-й) касается полей в зависимости от первичного ключа и только от первичного ключа.Правило предполагает, что первичный ключ был правильно идентифицирован (неправильно определить первичный ключ слишком легко).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top