【问题标题】:Is this a 1NF failure?这是 1NF 失败吗?
【发布时间】:2012-01-30 19:46:53
【问题描述】:

在考虑 1NF 故障时,没有重复的元素组,如果您想对重复组的数量设置限制怎么办?

例如,您希望学生只列出 3 个电话号码。不再。有如下表会被认为是 1NF 失败吗?

Student 1    Phone1    Phone2    Phone3
Sally        111-1111 222-2222   333-3333
John         555-5555 999-9999   NULL

您将创建一个限制。这是可接受的、高效的数据库设计吗?

按照 1NF 故障的要求,将电话号码放在单独的表格中会更好吗?如果它在单独的表中,您将如何创建每个用户 3 个数字的限制?

【问题讨论】:

标签: php mysql sql database normalization


【解决方案1】:

如果每个用户在单独的表中,您将如何创建 3 个数字的限制?

我假设一个学生可能有零个、一个两个或三个电话号码。

如果您的 SQL 产品支持完整的 SQL-92:

CREATE TABLE Students
(
 student_name VARCHAR(20) NOT NULL UNIQUE
);

CREATE TABLE StudentPhonebook
(
 student_name VARCHAR(20) NOT NULL
    REFERENCES Students (student_name), 
 phone_number CHAR(8) NOT NULL
    CHECK (phone_number LIKE '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'), 
 UNIQUE (student_name, phone_number)
);

CREATE ASSERTION students_max_three_phone_numbers
   CHECK (
          NOT EXISTS (
                      SELECT *
                        FROM (
                              SELECT student_name, COUNT(*) AS tally
                                FROM StudentPhonebook
                               GROUP 
                                  BY student_name
                             ) AS DT1
                       WHERE tally > 3
                     )
         );

MySQL 不支持任何风格的CHECK,并且没有 SQL 产品支持CREATE ASSERTION,因此上述约束可能必须使用过程代码编写,例如触发器。

出于兴趣,如果您的 SQL 产品支持行级 CHECK 约束(大多数情况下),可以使用 occurrence 属性和 BETWEEN 1 AND 3 约束,然后将此属性包含在键约束中,例如

CREATE TABLE StudentPhonebook
(
 student_name VARCHAR(20) NOT NULL
    REFERENCES Students (student_name), 
 phone_number CHAR(8) NOT NULL
    CHECK (phone_number LIKE '[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'), 
 occurrence INTEGER DEFAULT 1 NOT NULL
    CHECK (occurrence BETWEEN 1 AND 3), 
 UNIQUE (student_name, phone_number), 
 UNIQUE (student_name, occurrence)
);

【讨论】:

  • 在 MySQL 中,CHECK (occurrence BETWEEN 1 AND 3) 约束可以用 FOREIGN KEY 模拟到正好有 3 行的引用表。
【解决方案2】:

您的关系变量 (relvar) 确实违反了 1NF,但可能不是因为您期望的原因:违反 1NF 的是空值的存在。如果您认为您的 relvar 包含重复组,请三思。

第一范式,或简称为“规范化”,是关系模型的最低要求。引用 Chris Date 的话:

根据定义,null 不是值。随之而来的是:一种“类型” 包含 null 不是类型(因为类型包含值);一个“元组” 包含 null 的不是元组(因为元组包含值);一种 包含 null 的“关系”不是关系(因为关系 包含元组,元组不包含空值)。实际上,空值 违反了最基本的关系原则——即, 信息原则。所有这一切的结果是,如果空值是 现在,那么我们当然不是在谈论关系模型 (我不知道我们在说什么,但这不是关系 模型);整个大厦都倒塌了,所有的赌注都没有了。

关于重复组和 1NF 的要点很难解释,我不会尝试。相反,我强烈建议您阅读Facts and Fallacies about First Normal Form,特别是“重复组的歧义”部分。

假设 null 被消除,relvar 将满足 1NF,但请注意我们需要更多信息(例如键)来确定它是否也满足更高的范式。

【讨论】:

  • 此关系确实包含一个重复组,即电话号码组。简而言之,从上面的关系可以清楚地看出这一点。 NULL 值的存在使其更违反 1NF。这里的主要重点是创建一组需要数量有限的元素。
  • @user:我看到三个不同的属性分别命名为Phone1Phone2Phone3;它们可能被命名为home_phonemobile_phonework_phone,名称并不重要,与它们的类型相同。我没有看到“电话号码组”。
  • 在逻辑数据模型中,如果实体中的列中的数据类型相似,则根据定义它们是重复组。这同样适用于 Hobby1、Hobby2、Hobby3 或 Sport1、Sport2、Sport3。
  • 不管怎样,我们只是在这里扯皮。无论如何,根据数据库的设计,这很重要,但我明白了你的意思,我很欣赏 Chris Date 参考。他对正常形式有很多话要说。干杯。
  • "如果实体中的列中的数据类型相似,则根据定义,它们是重复组" -- 假设员工 relvar 具有 birth_datehire_date 的属性,两者都是日期类型: 是重复组吗?当然不是。您对“重复组”的定义不正确。再次阅读Facts and Fallacies 文章 :) 我不同意这是一个设计问题,但我不同意这是违反 1NF 或重复组。
【解决方案3】:

user1122200,假设您的数据库设计增长了。并且您需要为每个电话号码分配某些数据(例如电话位置:“房子”、“工作”……)。在这种情况下,你会渴望一个电话桌。此外,假设您需要通过电话号码查找学生(例如有人打电话时的必胜客或出租车服务),那么在规范化设计中查询比此查询更容易:

select *
from students
where 
  Phone1 = '91112223' or
  Phone2 = '91112223' or
  Phone3 = '91112223'

【讨论】:

    【解决方案4】:

    1NF 禁止连续重复列表。你的设计违反了这一点,下面的设计也是如此:

    Student     Phones
    'John D'    '555-5555, 666-6666, 777-7777'
    'Sally S'   '111-1111, 222-2222'
    

    下面的设计会违反2NF,因为唯一的主键是Name, Phone,但是Address属性不依赖于Phone

    Name        Phone       Address
    'John D'    '555-5555'  '1 Square Village'
    'John D'    '666-6666'  '1 Square Village'
    'John D'    '777-7777'  '1 Square Village'
    'Sally S'   '111-1111'  '999 Flash City'
    'Sally S'   '222-2222'  '999 Flash City'
    

    下一个设计会违反 3NF,因为 AreaName 不依赖于 Name,而只依赖于 Area:

    Name        Area    Phone   AreaName
    'John D'    '555'   '5555'  '111name'
    'John D'    '666'   '6666'  '666name'
    'John D'    '777'   '7777'  '777name'
    'Sally S'   '111'   '1111'  '111name'
    'Sally S'   '222'   '2222'  '222name'
    

    即使您的设计违反了 1NF,它也是一个极好的选择。添加PhoneNumber 表的复杂性几乎没有道理。

    想想如果您遵守 1NF,对客户进行更新会变得多么困难。这些数字将在单独的表格中。因此,如果有人提交了包含更新的电话号码列表的表单,您将如何更改数据库?首先,您必须检索现有的数字列表。然后你必须将它们与提交的列表进行比较。然后您必须根据差异删除或插入行。一个复杂的解决方案。

    如果你坚持你的解决方案,你可以只更新三列。节省的时间可以花在真正的功能上!甚至在 Stack Overflow 上写长答案。

    【讨论】:

    • 哈哈。因此,即使重复组的数量有限制,似乎也会发生 1NF 故障,而不仅仅是没有设置限制的重复组。
    • 实际上,您只需删除他所有的电话号码,然后从表格中插入所有电话号码 - 或者您只需制作一个单独的表格(ajax 很好),您可以在其中删除单个电话号码和添加新的。所以总而言之,这在很大程度上取决于您的应用程序更舒适。当然,除非您使用 ORM,其中具有适当关系的单独表将为您提供用户电话号码的数组。
    • 正确。范式来自“关系代数”,有关详细介绍,请参阅Stanford free class。我会说范式是一个很好的工具,但你必须权衡成本和收益,而不是盲目地应用它。
    • @Andomar:您所说的 *NF 差异是正确的,但是我不同意将模式建模为 *NF 会导致复杂性的方法,为了发布功能。在 OP 中有 3 个电话字段,可以合理地假设他正在为用户提供的数据提供表单,因此电话链接表上的 CRUD 活动是可以容忍的。以我的经验,放弃 *NF 要糟糕得多。如果他的表包含数万条记录,并且他需要运行更改以添加另一个电话列,那将是痛苦的。
    • 我很少看到有人反对规范化。呃。
    【解决方案5】:

    不,它没有标准化。当存在空值时,您将浪费表中的空间,并且如果您想要执行诸如搜索特定电话号码之类的操作,则必须搜索所有三列。而是使用一个单独的表(例如,StudentPhoneNumbers)来存储它们。如果您想将其限制为三个,请使用触发器来防止每个学生超过三个。

    【讨论】:

    • 不在 2NF 或 3NF 中,但它在 1NF 中,这就是 OP 所要求的。
    • @user1122200 - 你为什么要问 SQL Server?你已经标记了 MySQL?
    • @user1122200 - 你似乎太敏感了。我没有在任何地方批评过这个问题。如果使用的 1NF 有不同的定义(Codd vs Date),那么这肯定是相关的。你似乎忽略了它说如果你使用 Date 的定义就是违规的部分。
    • 这似乎是一场学术性很强的辩论。归根结底,答案就是“不要这样做”。为什么在第 1 或第 2 或第 3 NF 时这是一个坏主意真的很重要?这仍然是个坏主意。
    • @user - 您还可以通过在StudentId,Number 上放置一个唯一约束并在Number 上放置一个检查约束,强制它在集合1,2,3 中,以声明方式管理此约束。仍然不是那么容易使用,但因为它是由 RDBMS 强制执行的,所以它可以避免由于触发器代码中的竞争条件而超过 3 限制。这需要一个支持检查约束的 RDBMS。 MySQL 没有。
    猜你喜欢
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多