外键的默认值与 null

Posted

技术标签:

【中文标题】外键的默认值与 null【英文标题】:default value vs. null for foreign key 【发布时间】:2011-08-06 08:09:34 【问题描述】:

我有一个关于在数据库中为外键列使用空值与默认值的问题。在设计数据库时,我发现很多关于 null 与默认值的相反意见,但并不完全适用于外键(主要优点和缺点是什么)。

目前我正在设计一个新的数据库,它将为不同的 Web 应用程序和具有不同数据访问方法(ORM、存储过程)的其他系统存储大量数据,并且我想在最低级别实现一般规则(数据库)。 (所以以后在应用程序中不必担心这些规则)。

举个例子,假设我有一个用户表User,外键列是他的国籍NationalityID,这是表CountryID的主键Country

现在我有两个/三个选项:

A:我允许NationalityID 列(以及数据库中所有其他类似的外键列)为空,并且只坚持始终检查空值的常用方法(在应用程序中应用规则)

B:我为每个外键分配了一个默认值,比如说“-1”,并在每个关系表的附加列中放入“-1”作为键,所有其他数据作为“无数据”(为此例如在Country 表中,我放置了 CountryID 为“-1”的列,对于CountryName,我设置了“无数据”)。所以每次我想知道用户的国籍时,我总是会在没有额外代码规则的情况下得到结果(我不需要检查它是否为空)。

C:我可以禁止外键为空值。但这确实是我想要避免的。 (如果不是附加数据(用户国籍),我需要至少存储基本数据(用户名)的选项)

那么 B 是不是好方法?我在这里想念什么?通过这种方法,我会失去更多吗?我可能会遇到哪些问题(除了要小心在关系表中始终有附加列,ID 值为“-1”,表示“没有数据”)?

您对外键默认值的好/坏体验是什么?

谢谢

【问题讨论】:

【参考方案1】:

如果你标准化这将不是问题。

不要将国籍放在USER 表中,而是创建一个User_Nationality 表,将用户链接到另一个表中的Country_ID

如果他们在该查找表中有条目,那就太好了。如果没有,则不需要为它存储NULL 或默认值。

您需要强制执行 FK 关系,而允许 NULL 与此相反。您也不想仅仅为了填充一个字段而编造可能不准确的信息,这否定了首先需要该字段的意义。

使用查找表,您可以完全绕过它。

这也将让您改变主意并选择您的选项之一。

如果您使用视图,您可以选择将缺失数据视为NULL 或默认值,而无需更改基础数据。

【讨论】:

我认为带有查找表的数据会过于分散。仅对于用户,我将需要至少 10 个用户查找表(我需要处理许多不同的数据,例如用户的国籍)。用户只是整个数据库的一小部分(我们需要处理有关工作、义务、活动、安全性等等的数据)。我很快就会有 100 多张桌子……这是我需要支付的价格吗? 这是最强大的解决方案。您将花费大量时间来创建它,但是一旦到位,维护起来就会容易得多。它还允许您在以后添加标准,而无需完全重新制作用户表。 如何巧妙地处理查找表中同一用户的多条记录问题?正如我所提到的,我希望将一般规则应用于最低的数据库级别。问题:如果我创建单独的表来存储用户的出生国家,例如,我需要应用一些额外的规则来避免有两个出生国家的人。而且,当我想插入用户出生国家时,我总是需要先检查出生国家的记录是否已经存在,然后处理插入或更新。对不起,如果我举了愚蠢的例子,希望你明白我想指出的:) @Janez - 非常非常非常简单 - 在查找表的 User_Id 字段上创建 PRIMARY KEYUNIQUE CONSTRAINT。这样,数据库引擎将只允许每个user_id 一行。 我知道约束,我认为它是最好的解决方案(至少在我的情况下)。我认为我将继续为我的数据库使用这种“查找表”方法。但是现在我还需要找到让数据库自动处理约束的方法:如果有人想为查找表中已经存在的用户插入值,请删除该表的插入并更新现有表(可能是触发器?)。这样,我将实现在数据库级别应用全局规则(无需在无数应用程序中以无数不同的方式单独处理它)。再次感谢......现在:)【参考方案2】:

就个人而言,我觉得即使您的数据库中有一个键为 -1 的非条目条目,您仍然会执行检查以查看是否要为每个条目显示“无数据”单个字段。

我会坚持使用 NULL。 NULL 表示没有数据,这里就是这种情况。

【讨论】:

我有很多场景返回给我的值,上面写着“没有数据”就足够了(不需要额外的代码,需要检查)。无论如何,我将使用查找表方法,谢谢指出这一点【参考方案3】:

B 是一种糟糕的方法。记住处理空值比必须弄清楚你使用了什么幻数然后你仍然必须处理它们更容易。使用数字 1。但我最喜欢 JNK 的想法。

【讨论】:

【参考方案4】:

我建议选项 D。如果不是所有用户都具有已定义的国籍,则该信息不属于用户表。创建一个名为 UserNationality 的表,以 UserId 为键。

【讨论】:

这也是 JNK 还建议的查找表方法。谢谢【参考方案5】:

我喜欢你的 B 解决方案。也许可以将值映射到其他实体,因此您有 Country 和 NullCountry,它扩展 Country 并映射到 id=-1 的行,并且在其方法中有特殊代码以便于处理特殊情况。

一个问题可能是在该外键上进行外部连接会更加困难。

编辑:不,外连接应该没有问题,因为不需要进行外连接。

【讨论】:

以上是关于外键的默认值与 null的主要内容,如果未能解决你的问题,请参考以下文章

设置外键的默认值?

Django 模型:选择外键的默认值

SQL:使用NULL值与默认值

传递默认参数值与根本不传递参数?

主键约束,外键约束,空值约束,默认值约束,唯一约束,检查约束的各个作用是啥?

如何确定键的“(默认)”值