Sql 6NF中引用完整性的复合键与代理键

Sql 6NF中引用完整性的复合键与代理键,sql,database,6nf,Sql,Database,6nf,获取三层信息: 第1层:信息 该层包含具有唯一自然索引的数据和易于传输的代理键 Table Surnames: +-----------------------------+--------------+ | ID (Auto Increment, PK) | Surname | +-----------------------------+--------------+ | 1 | Smith | |

获取三层信息:

第1层:信息 该层包含具有
唯一
自然索引的数据和易于传输的代理键

Table Surnames:

+-----------------------------+--------------+
|    ID (Auto Increment, PK)  |    Surname   |
+-----------------------------+--------------+
|               1             |     Smith    |
|               2             |    Edwards   |
|               3             |     Brown    |
+-----------------------------+--------------+

Table FirstNames

+-----------------------------+--------------+
|   ID (Auto Increment, PK)   |   FirstName  |
+-----------------------------+--------------+
|               1             |     John     |
|               2             |     Bob      |
|               3             |     Mary     |
|               4             |     Kate     |
+-----------------------------+--------------+
自然关键点

create table people (
  FirstName integer not null
    references FirstNames (ID),
  LastName integer not null
    references Surnames (ID),
  primary key (FirstName, LastName)
);
create table people (
  FirstName varchar(n) not null
    references FirstNames (FirstName),
  LastName varchar(n) not null
    references Surnames (Surname),
  primary key (FirstName, Surname)
);
或者,上面的两个表可以没有
ID
,并使用姓氏和名字作为自然主键,如Mike Sherrill所解释的。在本例中,假设下面的层引用的是
varchar
,而不是
int

第二层:人 在这一层中使用了一个综合索引。此值可以是
唯一的
主键
,具体取决于是否将代理键用作主键

+-----------------+--------------+
|    FirstName    |    LastName  |
+-----------------+--------------+
|        1        |       2      |
|        1        |       3      |
|        2        |       3      |
|        3        |       1      |
|        4        |       2      |
|       ...       |      ...     |
+-----------------+--------------+
第三层:父母 在这一层中,人与人之间的关系通过
ParentsOf
表进行探索

ParentsOf

+-----------------+-----------------+
|      Person     |   PersonParent  |
+-----------------+-----------------+

 OR

+-----------------+-----------------+-----------------+-----------------+
| PersonFirstName |  PersonSurname  | ParentFirstName |  ParentSurname  |
+-----------------+-----------------+-----------------+-----------------+
问题 假设引用完整性在其核心对我来说非常重要,我将在这些索引上有
外键
,这样我就可以让数据库负责在这方面监控其自身的完整性,并且,如果我要使用ORM,它将是一个对复合主键具有本机支持的ORM

请帮助我理解:

  • 在第1层上使用代理关键点与自然关键点的权衡列表

  • 在第二层上使用复合键与代理键(可以转移到第三层)的权衡列表

我对听哪个更好不感兴趣,因为我知道专业人士在这个问题上存在重大分歧,这将引发一场宗教战争。相反,我要问的是,非常简单且尽可能客观地问,通过向每个层传递代理关键点与维护主键(自然/复合或代理/复合),您将采取什么样的权衡。任何人都可以在SO和其他网站上找到说“从不”或“始终”使用代理键”的人。相反,在你的回答中,我最欣赏的是对权衡的理性分析

编辑:有人指出,姓氏示例对于6NF的使用是一个糟糕的示例。为了保持问题的完整性,我将不提这个问题。如果您在想象这个用例时遇到困难,更好的方法可能是列出“杂货项目”。又名:

自然组合键示例:

+-----------------------------+--------------+
|           Grocery           |   Brand      |
+-----------------------------+--------------+
|           Sponges           | Bright       |
|           Ice Cream         | Ben & Jerry's|
|           Ice Cream         | Store Brand  |
|           Tomato Soup       | Campbell's   |
|           Tomato Soup       | Store Brand  |
|           Lemons            | Store Brand  |
|           Whipped Cream     | Cool Whip    |
+-----------------------------+--------------+ 
推荐配对

+-----------------+-----------------+-----------------+-----------------+
|     Grocery1     |  Brand1        | Grocery2        |  Brand2         |
+-----------------+-----------------+-----------------+-----------------+
重申一下,这也只是一个例子。我不建议这样做,但这应该有助于说明我的问题

这种方法有不足之处。我要重申,这个问题是要求我们仔细分析下面每种方法的优缺点,而不是强调一种方法比另一种方法好。我相信大多数人都能够通过这个具体例子的可疑性质来回答核心问题。此编辑适用于无法编辑的对象

下面有一些很好的答案,如果你想知道该往哪个方向走,请阅读

结束编辑


谢谢大家!

以下是一些权衡:

单个代理(人工创建):
  • 所有子表外键只需要一列来引用主键

  • 非常容易更新表中的自然键,而无需使用外键更新每个子表

  • 较小的主键/外键索引(即不宽)这可以使数据库运行更快,例如,当父表中的记录被删除时,需要搜索子表以确保不会创建孤立项。窄索引的扫描速度更快(非常明显)

  • 您将拥有更多索引,因为您很可能还希望索引数据中存在的任何自然键

自然复合键控表:
  • 数据库中的索引更少

  • 数据库中的列更少

  • 插入大量记录更容易/更快,因为您无需获取序列生成器

  • 更新组合中的一个键需要更新每个子表

还有另一类:人工合成主键 我只找到了一个有意义的例子。当您需要标记每个表中的每个记录以实现行级安全性时

例如,假设您有一个存储50000个客户机数据的数据库,而每个客户机都不应该看到其他客户机的数据——这在web应用程序开发中非常常见

如果每个记录都标记有
客户机id
字段,则您正在创建一个行级安全环境。大多数数据库都具有在正确设置时强制执行行级安全性的工具

首先要做的是设置主键和外键。通常,具有
id
字段作为主键的表。通过添加
client\u id
密钥现在是复合密钥。并且必须将
客户机id
携带到所有子表中

复合密钥基于2个代理密钥,是一种防弹方法,可确保客户端之间以及整个数据库内的数据完整性

在此之后,您将创建视图(或者如果使用Oracle EE设置虚拟专用数据库)和其他各种结构,以允许数据库强制执行行级安全性(这是它自己的主题)

假定此数据结构不再规范化为第n级。每个pk/fk中的
client_id
字段对正常模型进行反规范化。该模型的好处是易于在数据库级别强制执行行级别的安全性(这是数据库应该做的)。每次选择、插入、更新和删除都仅限于当前设置的会话。该数据库具有create table people ( FirstName varchar(n) not null references FirstNames (FirstName), LastName varchar(n) not null references Surnames (Surname), primary key (FirstName, Surname) );
create table people (
  ID serial primary key,
  FirstName integer not null
    references FirstNames (ID),
  LastName integer not null
    references Surnames (ID),
  unique (FirstName, LastName)
);
create table people (
  ID serial primary key,
  FirstName varchar(n) not null
    references FirstNames (FirstName),
  LastName varchar(n) not null
    references Surnames (Surname),
  unique (FirstName, Surname)
);
create table ParentsOf (
  PersonFirstName integer not null,
  PersonSurname integer not null,
  foreign key (PersonFirstName, PersonSurname)
    references people (FirstName, LastName),

  ParentFirstName integer not null,
  ParentSurname integer not null,
  foreign key (ParentFirstName, ParentSurname)
    references people (FirstName, LastName),

  primary key (PersonFirstName, PersonSurname, ParentFirstName, ParentSurname)
);
create table ParentsOf (
  PersonFirstName varchar(n) not null,
  PersonSurname varchar(n) not null,
  foreign key (PersonFirstName, PersonSurname)
    references people (FirstName, LastName),

  ParentFirstName varchar(n) not null,
  ParentSurname varchar(n) not null,
  foreign key (ParentFirstName, ParentSurname)
    references people (FirstName, LastName),

  primary key (PersonFirstName, PersonSurname, ParentFirstName, ParentSurname)
);
create table ParentsOf (
  Person integer not null
    references People (ID),
  PersonParent integer not null
    references People (ID),
  primary key (Person, PersonParent)
);
update surnames
set surname = 'Smythe'
where surname = 'Smith';
CHECK ( jobtitle <> 'BOSS' OR salary > 100 )
ID   Name        Value       DeletedFlag
1    Smith       78          0
2    Martin      98          0
3    John        78          1
4    Edouard     54          0
5    John        64          0
STUDENT     COURSE
1           CS101
1           CS101