当表中的每一列都是外键时,它是好还是过度?

时间:2012-02-12 00:03:04

标签: sql primary-key foreign-key-relationship

我正在建立一个可能的车辆数据库,每个条目都有一个Make,Model,Year和Engine。

我把它分成了制表(福特,雪佛兰,宝马等)和模型(Impala,Camaro,F-150等)和年(1920,...... 2012)和引擎(327,350,等)。

由于我现在每个Make,Model,Year和Engine都有一个表,并且它们每个都有唯一的主键,主“MakesModelsAndYears”表中的每一行都只由四个外键组成。

这是否过度,或者真正存储的效率高于我创建唯一索引的一个大表?我对“一个大桌子”方法的关注是像1970年这样的年代会重复多次(1970年雪佛兰Impala,1969年雪佛兰Camaro等),因为它有模型甚至引擎。

感谢任何指导!

enter image description here

跟进:

对于接下来的人,我将反馈结合到答案中并得出了这个模式。图像没有详细显示FK,但它们实际上是答案的建议:

enter image description here

4 个答案:

答案 0 :(得分:5)

如果表格中包含2,3,4或更多外键,并且主键是这些FK的组合(如果适合您的模型),则完全没有问题。

我在这个设计中遇到的唯一问题是它允许一个" BMW Escort"或者"福特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

答案 1 :(得分:4)

雪佛兰不会制造野马。福特在1960年没有制造野马。你的结构将允许很多废话。

问题不在于每列都是外键;这没有什么不妥。问题是外键是错误的。

  

我把它分成了制表(福特,雪佛兰,宝马等)和模型(Impala,Camaro,F-150等)和年(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}。 make和model的正确“查找”表包括make和model;您可以选择{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,除非它的行首先存在于模型中。并且没有行可以进入模型,除非它的行首先存在于make中。

您可以在这样的架构中使用ON UPDATE CASCADE。您也可以为不使用它做好准备。 Oracle不支持ON UPDATE CASCADE,这是您看到ID表中的Oracle数据库的一个原因,以及为什么有时会看到人们说“主键值必须从不更改”。

这些是您实现已知要求所需的表格类型。

答案 2 :(得分:0)

PK / FK关系是一个非常强大的关系,可以为您在数据库中提供机会。话虽如此,但并不总是合适的。其中很大程度上取决于数据库以及如何使用它。上述数据的单个表将有助于更快的数据访问和更轻松的报告,但它牺牲了可扩展性和单实例数据存储。

对于您的具体情况,我建议将这些年份放回主表中。由于这一年是一个int,重复并不是什么大问题。我还要说你应该将你的模型表链接到你的品牌表,因为品牌已连接到模型。

答案 3 :(得分:0)

乍一看,我看到所有这些桌子毫无意义。也许MakesModelsAndYears就是你所需要的。然后我会重新考虑它的名字。至少我会放弃“和”。我最多将其重命名为“汽车”

表格键和数据关系

不一定是同一件事。主键唯一标识给定表的行。就这样。外键是某个其他表上存在给定值的“保证”。除了正式定义的密钥之外,数据可以相关。我们有时称这些候选键。哦,没有法律规定你必须在任何给定的表中都有主键。

我使用过数据库,我经常在主键和/或外键之外加入表。这就是真正的数据关系如何实现的。

数据规范化

作为一般规则,您希望避免跨表的数据冗余。但是,如果您的Years表格行只有一列 - “年”,那么它的重点是什么(同样适用于其他表)?您基本上会在指向它的MakeModelsAndYears中复制该数据。

如果您确实保留ModelEngineYearMakes表格不会造成愚蠢,愚蠢的错误Makes表中的“ID”列(所有表格同上),这样您就不必在“{1}}表格中存储”Chevrolet“。想象一下,看着那张桌子,你看到的只有一排排的数字!要显示有意义的信息,您必须进行大量的联接 - 只需说“1960 Chevy 454 Hemi Impala”。现在效率很低!

<强>索引

您所做的索引取决于您在查看数据时如何查找数据以及数据库的性能。

特别为您可能加入或搜索但不是正式密钥的列创建索引。

此外,多列索引非常适合您经常一起搜索的列。索引列顺序很重要。当我所做的只是在某个索引中反转列顺序时,我看到了一个非常显着的差异。您的里程可能会有所不同(双关语意)。是的,根据表大小,连接列顺序,查找频率,相同列上不同排序的索引可能有意义。如果/遇到性能问题,您只需要查看这些内容。