您可以在表中定义外键,从中派生具有主键的表吗?

时间:2019-11-29 10:52:23

标签: mysql database-design

假设我有一个名为“学生”的表格。

此表包含列

  • StudentId(主键)
  • StudentName,
  • StudentSurName。

让我们想象一下,我从该表中创建了另一个表“ OriginSurnames”,其中包含“ Students”表中学生的不同姓氏的来源国。

此表具有以下列:

  • StudentSurName(主键)
  • 来源

在表“ Students”中声明StudentSurName是否有意义,如果我在表“ Students”中不存在“ OriginSurnames”项,那么我不能在该表中输入“ StudentSurName”。

2 个答案:

答案 0 :(得分:2)

问题

技术上的答案很简单,但是问题在于您自己的理解程度。

了解数据(建模的目的)和设计数据库(建模的最后一步)是一门科学,具有一整套方法和过程,自1970年以来就没有改变。它是Logic的应用,是的,但专门用于数据。

编写代码,程序设计,良好实践;等是完全不同的科学。尽管它是逻辑的另一种应用。如果一个人擅长一门科学,那么他们通常却不擅长另一门科学。一个人同时兼具两种能力是非常罕见的……现代程序员无论如何都会在不了解其相关性的情况下做到这一点。它总是会导致问题。


数据

  

在表Student中声明StudentSurName是否有意义,这是因为如果表“ Students”中不存在“ OriginSurnames”表,则我无法在该表中输入“ StudentSurName”。

您会感到困惑,因为您没有将数据(以及必须考虑的规范化和设计问题)与程序(以及应该做什么以及何时分开)分开。

让我们把数据当作数据,而仅将数据作为数据。

  • 一个简单的事实是,OriginSurname包含每个Surname的一个实例,而Student包含许多Surname的实例。因此,该关系是OriginSurname.Surname与许多Student.Surnames的关系。

  • 现在表示数据库中的一致性。您希望OriginSurname.Surname仅在存在一个或多个Student.Surnames时才存在。那就是基数。因此,该关系是一对OriginSurname.Surname与一对Student.Surnames(而不是一对多)。

此时不必担心如何获得该信息,因为这与程序有关,与数据库无关,并且数据库是数据的独立,自描述和集成存储。


解决方案•初始

它看起来像这样:

alvyTA1

注意•表示法

  • 我所有的数据模型均以 IDEF1X 呈现,这是自1993年以来建立关系数据库建模的标准

  • 我的 IDEF1X Introduction 对初学者来说是必不可少的阅读内容。

注意•内容

您有一个良好的数据库表OriginSurname和一个可怕的Student文件。为什么呢因为当您将一个Record ID声明为主键时,可以确保Record ID是唯一的,但是您没有什么可以使数据行保持唯一。 关系模型要求表中的逻辑行(与记录相对)是唯一的。

这应该失败,我们希望它失败,但它会成功:

INSERT Student VALUES( "John", "Smith" ) -- should succeed
INSERT Student VALUES( "John", "Smith" ) -- duplicate row: should fail
INSERT Student VALUES( "John", "Smith" ) -- duplicate row: should fail

解决方案•已更正

对于给定的列,您需要在( Surname, Name )上建立索引。现在,行(无论Record ID是什么)都是唯一的。现在第二个John Smith将失败。看起来像这样:

alvyTA2

最后...

  

在学生表中声明StudentSurName是否为外键[引用OriginSurname表中的PK]是否有意义

是的。绝对。这就是数据库平台如何在数据库中维护引用完整性。相反,如果不声明外键,则OriginSurname.SurnameStudent.Surname之间将没有完整性。


程序

现在,我们将考虑维护数据库所需程序的注意事项,以及我们在数据库中定义的特定一致性。也就是说,一致性首先在数据库中定义,其次由与数据库表交互的相关程序组件维护。

  

姓氏不能 [必须]在OriginSurnames表中不存在,如果在“ Students”表中没有人

同意。

  

因为在将具有该姓氏的学生插入到“学生”表中之后,将姓氏插入到“表OriginSurnames”表中。

那是错误的想法。在SQL中,自1981年以来,我们进行过 ACID交易。我们不会通过直接INSERT/UPDATE/DELETE向数据库中写入任何内容,因为这会使数据库处于状态一致,尤其是在程序或SQL平台或计算机系统出现故障的情况下。所有写入均已正确打包并一起写入事务中。

  1. 首先要了解,数据库必须一致;然后;中;然后写入数据库。那就是 ACID 中的 C

    • 在我们的定义中,正确的是,OriginSurname是父行,没有父项的子项Student是不行的。

    • 因此,当将新的Surname添加到Student时,必须INSERTed是父OriginSurname的{​​{1}},然后是INSERT的学生。

    • 同样,当学生为DELETEd时,如果这是该Surname的最后一次出现,则DELETE Student之后必须带有DELETE OriginSurname。

  2. A 代表原子。这个想法是,与我们一样,一个Transaction包含多个对数据库的写操作,并且该Transaction被完全应用或根本不应用。因此是原子的。事务用BEGIN TRANCOMMIT TRAN括起来(如果需要代码退出部分应用的事务,则用ROLLBACK TRAN括起来。)

Student_Add事务将如下。我提供了框架代码,您需要添加错误检查等:

BEGIN TRAN
IF NOT EXISTS ( SELECT 1  
        FROM OriginSurname     -- test if parent not exists
        WHERE Surname = @Surname
        )
    INSERT OriginSurname       -- insert parent first
        VALUES( @Surname, @Origin )
INSERT Student                 -- insert child second
    VALUES( @Name, @Surname )
COMMIT TRAN

Student_Drop交易如下:

BEGIN TRAN
DELETE
    FROM  Student              -- delete child first
    WHERE Name = @Name
    AND   Surname = @Surname
IF NOT EXISTS ( SELECT 1
        FROM Student           -- test if parent obsolete
        WHERE Surname = @Surname
        )
    DELETE
        FROM  OriginSurname    -- delete obsolete parent second
        WHERE Surname = @Surname
COMMIT TRAN

您的注释中的“之前”或“之后”根本不适用,因为我们正在执行一个原子事务,在该事务中我们安排SQL动词以匹配数据库的定义,并且始终保持数据库的一致性。 / p>

ACID交易

要完成ACID交易,请执行以下操作:

  • 代表隔离
    在交易期间,即在BEGIN TRANCOMMIT TRAN之间,正在写入的数据库更改尚未完成。我们不希望其他活动数据库用户看到此不完整的数据,正是由于事务是原子的,他们必须继续仅看到一致的数据。因此,交易将由平台与其他用户隔离执行。除了了解那是如何发生的,您无需执行任何操作。正是在这个很小的时期内,资源被锁定,并且并发成为一个问题(在某种程度上,OLTP标准并未减轻它的负担)。

  • D 代表耐用
    每个交易必须是持久的,即。一旦执行COMMIT,事务就必须保留在数据库中,并且不得由于系统崩溃或炸弹摧毁建筑物而丢失。这是SQL平台本身不提供的功能,您作为编码器无需执行任何操作。每个平台都提供各种恢复工具,DBA必须对其进行设置,以确保业务需要从硬崩溃中恢复。

触发

请勿使用触发器。它们造成的问题比他们声称要解决的问题多得多。更重要的是,如果您按照建议使用事务(并且不直接写入数据库),则不需要它们。


免费软件“ sql”

关系模型问世之后的头三个十年,以及SQL的数据子语言,所有SQL平台都是真实的平台(服务器体系结构;完全符合SQL标准;各种恢复和耐用性设施;等)。自从1981年首次发布SQL标准以来,ACID事务就一直存在。

在20世纪90年代,各种假装的SQL套件作为免费软件提供。共享软件;蒸气软体;没有软件这些是米老鼠套件(我们不能称其为平台),没有服务器体系结构;没有体面的备份或恢复;并且不符合SQL标准。这意味着他们使用 SQL 一词是欺诈行为。充其量它们是伪装的SQL。

PusGre NON sql
没有ACID交易。作为一种有点看起来很像的功能,这些功能声明为“事务性”。因此,缺少标准SQL BEGIN/COMMIT/ROLLBACK TRAN动词,因此您必须将该代码放入函数中。希望在线用户不太多。

我的 sql
确实有事务,但是它们不符合SQL,即。它们不是真正的ACID。对于您正在做的简单工作来说,它就足够了。 NONsql动词是BEGIN WORK。并且您必须将表设置为TYPE = InnoDB


答案 1 :(得分:0)

  • 对于一个独特的学生,只有一个姓氏
  • 要获得唯一的姓氏,可以有很多个学生

这是一对多关系。在这种关系中,具有最低关系(“ 仅一个 ”)的实体持有该关系(外键)

回答您的问题“在表Students中将StudentSurName声明为外键[...]有意义吗?” ,这不仅有意义,而且这是良好做法

这提供了:

姓氏:

  • SurNameId(主键)
  • StudentSurName(可以在其中使用唯一索引以确保没有重复项)
  • 来源

建议使用不包含任何信息的数字PK。例如,如果您输入错误,则可能需要编辑StudentSurName。不应编辑PK。

学生:

  • StudentId(主键)
  • StudentName
  • SurNameId(SurNames.SurNameId的外键)

关于SurNames,此表的设计仍然可以改进。

  • 对于唯一的姓氏,仅一个起源
  • 对于独特的起源,可以有很多个姓氏

最后以:

来源:

  • OriginId(主键)
  • 来源(可以在其中使用唯一索引以确保没有重复项)

姓氏:

  • SurNameId(主键)
  • StudentSurName(可以在其中使用唯一索引以确保没有重复项)
  • OriginId(Origins.OriginId的外键)