n:1 到两个 n:1,这个 table 正常化了吗?

n:1 to two n:1, is this table normalized properly?

感谢您的关注。自从我在学校和大学学习规范化以来已经有一段时间了,如果你去工业界工作,你会发现这些理论上的东西往往会干扰你老板让你做的事情。所以这是我考虑了一段时间的事情的一个例子,我很想听听你的意见。我什至不确定这种方法是否正确,所以欢迎大家回答。

场景: 我们正在开发人员管理系统 (PMS)。为此,我们有一个 Person table,用于存储每个人的一般信息(例如姓名),一个 Country table,用于存储一个人的祖国和一个 Race table人的种族。

因此你可以在 Person 上有两个外键列,链接到 Country 和 Race,对吧?

但是,我的老板要我使用第四个 table。让我们称之为 PersonType。 PersonType 由一个主键以及指向 Country 和 Race 的外键列组成。 Person table 然后只有一个指向 PersonType 的外键列。

因此我会在 Person 和 PersonType 之间建立 n:1 关系,在 PersonType 和 Country/Race 之间建立 n:1 关系,对吗?

在我看来,PersonType table 不是必需的,因为您可以直接将外键列放在 Person table 上,但我的老板认为 PersonType 可以用来限制哪些Country/Race 组合有效。我理解这个论点,但我在问自己这个数据库是否仍然正确规范化。

(当然,我们并不是真的在开发 PMS,但我认为这很容易想象,由于 NDA,我不能谈论我们真正在开发什么)。

2016 年 10 月 21 日更新

下面是 table 结构的抽象形式:

table person_v1(
  person_id int primarykey,
  name string,
  country_id int foreignkey(country),
  race_id int foreignkey(race)
)

table person_v2(
  person_id int primarykey,
  name string,
  person_type_id int foreignkey(person_type)
)

table person_type(
  person_type_id int primarykey,
  country_id int foreignkey(country),
  race_id int foreignkey(race)
)

table country(
  country_id int primarykey,
  name string
)

table race(
  race_id int primarykey,
  name string
)

感谢您到目前为止的回答

让我们假设这个 table 对您和您老板的设计都很常见:

CREATE TABLE RaceMeetings
( country_name VARCHAR(30) NOT NULL,
  race_name VARCHAR(25) NOT NULL,
  UNIQUE ( country_name, race_name ) );

据我所知,你的设计是这样的(一个table):

CREATE TABLE People_v1
( person_name VARCHAR(35) NOT NULL UNIQUE, 
  country_name VARCHAR(30) NOT NULL,
  race_name VARCHAR(25) NOT NULL,
  FOREIGN KEY ( country_name, race_name )
     REFERENCES RaceMeetings ( country_name, race_name ) );

...而你老板的设计是这样的(两个table):

CREATE TABLE People_v2
( person_name VARCHAR(35) NOT NULL UNIQUE );

CREATE TABLE RaceMeetingAttendance
( person_name VARCHAR(35) NOT NULL UNIQUE
     REFERENCES People_v2 ( person_name ),
  country_name VARCHAR(30) NOT NULL,
  race_name VARCHAR(25) NOT NULL,
  FOREIGN KEY ( country_name, race_name )
     REFERENCES RaceMeetings ( country_name, race_name ) );

两种设计

  • 完全标准化
  • 属于 5NF
  • restrict { country_name, race_name } 组合在 table RaceMeetings.
  • 中定义的人

你老板的设计也符合 6NF,但这并不一定具有任何实际优势。

但是,我确实更喜欢您老板的设计,该设计基于经验法则,即 table 应该为实体或实体之间的关系建模,但绝不能同时为两者建模。换句话说,像 "The race 'Pacific Grand Prix' is held in Japan" 这样的比赛并没有让我觉得是一个人的属性。在我看来,它更像是一种关系(从人到比赛会议的通信)并且使用单独的 table 来模拟这种关系可以让我有效地给它命名为“RaceMeetingAttendance”。

也就是说,您的设计的优点是,如果有要求,您的数据库中就不会存在没有参加过比赛的人。

1:N 或 M:N 关系的数量不决定关系的正常形式。这个问题其实和归一化无关。

一些 table。 . .

您的设计

尽可能遵循标准。 对于国家/地区,我将遵循 ISO 3166-1。

create table countries (
  iso_country_code char(3) primary key,
  country_name varchar(75) not null unique
);

insert into countries (iso_country_code, country_name) values
('USA', 'United States of America'),
('GBR', 'United Kingdom of Great Britain and Northern Ireland'),
('MKD', 'Macedonia (the former Yugoslav Republic of)'),
('ZZZ', 'Unknown country'); -- 'ZZZ' is reserved for a user-assigned value.

对于比赛,我将遵循 CDC/HL7 比赛守则。 还有其他标准。 其中之一可能更合适。 参见 http://www.cdc.gov/nchs/data/dvs/race_ethnicity_codeset.pdf

大多数应用程序允许每个人使用多个种族代码。 对于这个问题,我忽略了现实世界的事实。

create table races (
  cdc_unique_id char(6) primary key,
  cdc_race_concept varchar(50) not null unique
);

insert into races (cdc_unique_id, cdc_race_concept) values
('2056-0', 'Black'),
('2106-3', 'White'),
('2076-8', 'Native Hawaiian or other Pacific islander'),
('zzzz-z', 'Unknown');

create table persons (
  person_id integer primary key,
  person_full_name varchar(25) not null,
  iso_country_code char(2) not null 
    default 'ZZZ' 
    references countries (iso_country_code)
      on update cascade
      on delete set default,
  cdc_unique_id char(6) not null
    default 'zzzz-z'
    references races (cdc_unique_id)
      on update cascade
      on delete set default
);

所有这三个 table 都至少在 5NF 中。

您的设计存在一个潜在问题 是它允许任意配对 国家和种族。 想象一下,而不是国家和种族, 我们谈论的是美国的城市和州。 城市和州的任意配对将允许 "San Francisco, AL"。 但是阿拉巴马州没有名为 "San Francisco" 的城市。

这就是为什么允许任意配对可能是一个错误的决定。

你老板的设计

-- The same as the table above.
create table countries (
  iso_country_code char(3) primary key,
  country_name varchar(75) not null unique
);

insert into countries (iso_country_code, country_name) values
('USA', 'United States of America'),
('GBR', 'United Kingdom of Great Britain and Northern Ireland'),
('MKD', 'Macedonia (the former Yugoslav Republic of)'),
('ZZZ', 'Unknown country'); -- 'ZZZ' is reserved for a user-assigned value.

-- Also the same as the table above.
create table races (
  cdc_unique_id char(6) primary key,
  cdc_race_concept varchar(50) not null unique
);

insert into races (cdc_unique_id, cdc_race_concept) values
('2056-0', 'Black'),
('2106-3', 'White'),
('2076-8', 'Native Hawaiian or other Pacific islander'),
('zzzz-z', 'Unknown');

-- This table is new.
create table person_types (
  iso_country_code char(3) not null
    default 'ZZZ'
    references countries (iso_country_code)
      on update cascade
      on delete set default,
  cdc_unique_id char(6) not null
    default 'zzzz-z'
    references races (cdc_unique_id)
      on update cascade
      on delete set default,
  primary key (iso_country_code, cdc_unique_id)
);

insert into person_types values
('USA', '2016-3'),
('USA', '2056-0'),
('GBR', '2016-3'),
('GBR', '2056-0'),

这个"person_types"table记录一个事实 您的 数据库设计没有。 它记录了事实 白人和黑人 可能原产于美国和英国。 如果记录这一事实很重要,您必须包括"person_types"。

此外,值得注意的是 table 不受其他评论中提到的问题的影响;您不能重复添加 iso_country_code 和 cdc_unique_id 都为空的行(not null 约束),您不能复制 iso_country_code 和 cdc_unique_id(primary key 约束), 等等

从概念上讲,您在规范化之前决定要存储哪些事实。规范化无法帮助您处理模式中不存在的属性。那是一个不同的数据库设计任务。

-- Structurally identical to the table above.
-- Constraints are slightly different.
--
create table persons (
  person_id integer primary key,
  person_full_name varchar(25) not null,
  iso_country_code char(2) not null
    default 'ZZZ',
  cdc_unique_id char(6) not null
    default 'zzzz-z',
  constraint person_types_fk foreign key (iso_country_code, cdc_unique_id)
    references person_types (iso_country_code, cdc_unique_id)
      on update cascade
      on delete set default
);

所有这四个 table 都至少在 5NF 中。

区别不在于一组 table 比另一组更标准化。

区别在于一组table记录了一个事实,而另一组没有