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记录了一个事实,而另一组没有。
感谢您的关注。自从我在学校和大学学习规范化以来已经有一段时间了,如果你去工业界工作,你会发现这些理论上的东西往往会干扰你老板让你做的事情。所以这是我考虑了一段时间的事情的一个例子,我很想听听你的意见。我什至不确定这种方法是否正确,所以欢迎大家回答。
场景: 我们正在开发人员管理系统 (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记录了一个事实,而另一组没有。