Это хорошо или излишне, когда каждый столбец в таблице является внешним ключом? - PullRequest
3 голосов
/ 12 февраля 2012

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

Я разбил это на таблицы Makes (Ford, Chevrolet, BMW и т. Д.) ИМодели (Impala, Camaro, F-150 и т. Д.) И Years (1920, ... 2012) и двигатели (327, 350 и т. Д.).

Так как у меня теперь есть таблица для каждого из Make, Model, Year и Engine, и каждый из них может иметь уникальный первичный ключ, каждая строка в главной таблице «MakesModelsAndYears» состоит только из четырех внешних ключей.

Является ли это избыточным или действительно хранится более эффективно, чем простоодна большая таблица, где я создал уникальные индексы?Меня беспокоит подход «одного большого стола», который заключается в том, что годы, например 1970 год, будут повторяться много раз (Chevrolet Impala 1970 года, Chevrolet Camaro 1969 года и т. Д.), Как если бы у них была модель и даже двигатель.

Спасибо за любые рекомендации!

enter image description here

Продолжение:

Для тех, кто следовал дальше, я включил обратную связь в ответы и пришел, что этосхемы.Изображение не показывает FK в деталях, но они фактически соответствуют предложенному ответу:

enter image description here

Ответы [ 4 ]

5 голосов
/ 12 февраля 2012

Нет проблем с таблицей с 2, 3, 4 или более внешними ключами, а первичный ключ является комбинацией этих FK, если это соответствует вашей модели.

Единственная проблема, которую я вижу с этим дизайном, состоит в том, что он допускает "BMW Escort" или "Ford Z4". Возможно, вы можете изменить дизайн на:

Makes
-----
Make PK


Models
------
Make  PK, FK to Makes
Model PK


MakesModelsAndYears
-------------------
Year       PK, FK1 to Years
Make       PK, FK2 to Model
Model      PK, FK2
EngineSize PK, FK3 to Engines
4 голосов
/ 12 февраля 2012

Шевроле не делает Мустанг.Форд не производил Мустанг в 1960 году. Ваша структура позволит много глупостей.

Проблема не в том, что каждый столбец является внешним ключом;в этом нет ничего плохого.Проблема в том, что внешние ключи неверны.

Я разделил это на таблицы Makes (Ford, Chevrolet, BMW и т. Д.) И Models (Impala, Camaro, F-150 и т. Д.) И Years (1920, ... 2012) иДвигатели (327, 350 и т. Д.).

И вот почему они не правы.Когда вы нормализуете отношение, вы начинаете с отношения, идентифицируете ключи-кандидаты и определяете функциональные зависимости.Простое создание таблиц подстановки из одного столбца для каждого столбца не является нормализацией и не ограничивает ваши данные требуемым образом.(И в этом конкретном случае ограничения - это недостающая часть, а не нормализация до 5NF.)

Make       Model   Yr    Engine
--
Ford       F-150   2012  3.7L V6
Ford       F-150   2012  3.5L V6 EcoBoost
Ford       F-150   2012  5.0L V8
Ford       F-150   2012  6.2L V8
Ford       F-150   2011  3.7L V6
Ford       F-150   2011  3.5L V6 EcoBoost
Ford       F-150   2011  5.0L V8
Ford       F-150   2011  6.2L V8
Chevrolet  Camaro  2012  3.6L V6
Chevrolet  Camaro  2011  3.6L V6
Chevrolet  Camaro  2011  6.2L V8
Chevrolet  Camaro  1980  229ci V6
Chevrolet  Camaro  1980  267ci V8
Chevrolet  Camaro  1980  305ci V8
Cadillac   CTS     2004  3.6L V6
Vauxhall   Astra   1979  1.3L
Vauxhall   Astra   1979  1.6L
Vauxhall   Astra   1979  1.8L
Opel       Astra   1979  1.5L
Opel       Astra   1979  2.0L

Должно быть ясно, что единственным подходящим ключом является {Make, Model, Yr, Engine}.Таким образом, эта таблица является ключевой и не имеет непростых атрибутов.

Чтобы добавить таблицы «поиска» в качестве ограничений на данные, недостаточно сказать, что в первом столбце вы должны выбрать из {Ford, Chevrolet, Cadillac, Vauxhall, Opel}, а во втором столбце вы должны выбрать из {F-150, Camaro, CTS, Astra}.Правильная таблица соответствия для марки и модели включает как марку, так и модель;Вы выбираете из {Ford F-150, Chevrolet Camaro, Cadillac CTS, Vauxhall Astra, Opel Astra}.(В этом случае это идет еще дальше. См. Таблицу model_years ниже.)

create table makes (
  make varchar(25) primary key
);

insert into makes values
('Ford'),
('Chevrolet'),
('Cadillac'),
('Vauxhall'),
('Opel');

create table models (
  make varchar(25) not null references makes (make),
  model varchar(25) not null,
  primary key (make, model)
);

insert into models values 
('Ford', 'F-150'),
('Chevrolet', 'Camaro'),
('Cadillac', 'CTS'),
('Vauxhall', 'Astra'),
('Opel', 'Astra');

create table model_years (
  make varchar(25) not null,
  model varchar(25) not null,
  year integer not null check (year between 1900 and 2050),
  primary key (make, model, year),
  foreign key (make, model) references models (make, model)
);

insert into model_years values
('Ford', 'F-150', 2012),
('Ford', 'F-150', 2011),
('Chevrolet', 'Camaro', 2012),
('Chevrolet', 'Camaro', 2011),
('Chevrolet', 'Camaro', 1980),
('Cadillac', 'CTS', 2004),
('Vauxhall', 'Astra', 1979),
('Opel', 'Astra', 1979);

create table model_year_engines (
  make varchar(25) not null,
  model varchar(25) not null,
  year integer not null,
  engine varchar(25) not null,
  primary key (make, model, year, engine),
  foreign key (make, model, year) references model_years (make, model, year)
);

insert into model_year_engines values
('Ford', 'F-150', 2012, '3.7L V6'),
('Ford', 'F-150', 2012, '3.5L V6 EcoBoost'),
('Ford', 'F-150', 2012, '5.0L V8'),
('Ford', 'F-150', 2012, '6.2L V8'),
('Ford', 'F-150', 2011, '3.7L V6'),
('Ford', 'F-150', 2011, '3.5L V6 EcoBoost'),
('Ford', 'F-150', 2011, '5.0L V8'),
('Ford', 'F-150', 2011, '6.2L V8'),
('Chevrolet', 'Camaro', 2012, '3.6L V6'),
('Chevrolet', 'Camaro', 2011, '3.6L V6'),
('Chevrolet', 'Camaro', 2011, '6.2L V8'),
('Chevrolet', 'Camaro', 1980, '229ci V6'),
('Chevrolet', 'Camaro', 1980, '267ci V8'),
('Chevrolet', 'Camaro', 1980, '305ci V8'),
('Cadillac', 'CTS', 2004, '3.6L V6'),
('Vauxhall', 'Astra', 1979, '1.3L'),
('Vauxhall', 'Astra', 1979, '1.6L'),
('Vauxhall', 'Astra', 1979, '1.8L'),
('Opel', 'Astra', 1979, '1.5L'),
('Opel', 'Astra', 1979, '2.0L');

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

Можно привести хороший пример использования ON UPDATE CASCADE в такой схеме.Вы также можете сделать хороший случай, чтобы не использовать его.Oracle не поддерживает ON UPDATE CASCADE, что является одной из причин, по которым вы видите ID-номера, попадающие в таблицы Oracle, и почему иногда вы видите, что люди говорят: «Значения первичного ключа не должны никогда изменяться».

Это таблицы, которые вам нужны для реализации ваших известных требований.

0 голосов
/ 12 февраля 2012

На первый взгляд я вижу все эти таблицы как бессмысленные.Возможно, MakesModelsAndYears - это все, что вам нужно.И тогда я бы пересмотрел его имя.По крайней мере, я бы бросил "И".В лучшем случае я бы переименовал его в «Автомобили»

Ключи таблиц и отношения данных

Не обязательно одно и то же.Первичный ключ однозначно идентифицирует строки данной таблицы.Это все.Внешний ключ - это «гарантия» того, что данное значение существует в некоторой другой таблице.Данные могут быть связаны не формально определенными ключами.Мы иногда называем эти ключи-кандидаты.О, нет закона, согласно которому у вас должен быть первичный ключ в любой данной таблице.

Я работал с базами данных, где я часто объединял таблицы, отличные от первичных и / или внешних ключей.Вот как сработали реальные отношения данных.

Нормализация данных

Как правило, вы хотите избегать данных избыточность между таблицами.Однако, если в вашей строке таблицы Years есть только один столбец - «год», то какой в ​​этом смысл (так же и для этих других таблиц)?По сути, вы дублируете эти данные в MakeModelsAndYears, указывая на него.

И если вы сохраняете таблицы Model, Engine, Year, Makes, то не делаете глупым, ошибка при создании столбца «ID» в вашей таблице Makes (то же самое для всех таблиц), так что вам не нужно хранить «Chevrolet» в таблице MakeModelAndYears.Представьте себе, что вы смотрите на эту таблицу, и все, что вы видите, это числа в ряду за строкой !!Чтобы отобразить значимую информацию , вам нужно будет сделать много соединений - просто сказать «Chevy 1960 454 Hemi Impala».Теперь это неэффективно!

Индексы

То, что вы создаете, зависит от того, как вы будете искать данные и производительность базы данных при этом.

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

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

0 голосов
/ 12 февраля 2012

Отношения PK / FK - это действительно мощные отношения, которые открывают для вас возможности в вашей базе данных. С этим, как говорится, это не всегда уместно. Многое зависит от базы данных и того, как вы будете ее использовать. Единая таблица для вышеуказанных данных поможет ускорить доступ к данным и упростить отчетность, но при этом жертвует масштабируемостью и хранением данных в одном экземпляре.

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

...