3

这原本是对此处另一个问题中提出的逻辑模式的“更新”:在 MySQL 中出现错误 1701、错误 1452 和错误 1305 - 需要一些专业知识......

我想我已经成功地将这个模式验证为第一和第二范式,但我不确定这是否符合第三范式。这是有问题的模型:

对第三范式的逻辑数据库模型的尝试

这是相关代码(注意:由于某种原因,我无法在 sql 代码中重新创建 1:1 关系,如上面的逻辑模型所示):

-- database_schema.sql.
-- This sql script creates the structure.
-- of the rugby club database.

DROP DATABASE IF EXISTS database_rugby;

CREATE DATABASE database_rugby;

USE database_rugby;

-- Create the "person" table.
--
-- This table has one:one relationships
-- with the parent, coach and player 
-- tables.
DROP TABLE IF EXISTS `person` ;
CREATE TABLE `person` (
  `personID` INT(5) NOT NULL AUTO_INCREMENT ,
  `firstName` VARCHAR(50) NOT NULL ,
  `lastName` VARCHAR(50) NOT NULL ,
  `dateOfBirth` DATE NOT NULL ,
  `streetAddress` VARCHAR(150) NOT NULL ,
  `suburbAddress` VARCHAR(150) NULL DEFAULT NULL ,
  `cityAddress` VARCHAR(150) NOT NULL ,
  `photo` BLOB NULL DEFAULT NULL ,
  PRIMARY KEY (`personID`))
ENGINE = InnoDB;

-- Create the "parent" table.
DROP TABLE IF EXISTS `parent` ;
CREATE TABLE `parent` (
  `parentID` INT(5) NOT NULL ,
  `personID` INT(5) NOT NULL ,
  PRIMARY KEY (`parentID`, `personID`), 
  FOREIGN KEY (`personID`) REFERENCES `person` (`personID`)
  ON DELETE CASCADE 
  ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "school" table.
DROP TABLE IF EXISTS `school` ;
CREATE TABLE `school` (
  `schoolID` INT(5) NOT NULL AUTO_INCREMENT ,
  `schoolName` VARCHAR(100) NOT NULL ,
  PRIMARY KEY (`schoolID`))
ENGINE = InnoDB;


-- Create the "player" table.
--
-- Inherits fields from the "person"
-- and "school" tables.
DROP TABLE IF EXISTS `player` ;

CREATE TABLE `player` (
  `playerID` INT(5) NOT NULL ,
  `personID` INT(5) NOT NULL ,
  `schoolID` INT(5) NOT NULL ,
  PRIMARY KEY (`playerID`, `personID`), 
    FOREIGN KEY (`personID`) 
    REFERENCES `person` (`personID`)
    ON DELETE CASCADE 
    ON UPDATE CASCADE, 
    FOREIGN KEY (`schoolID`)
    REFERENCES `school` (`schoolID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "coach" table.
DROP TABLE IF EXISTS `coach`;
CREATE TABLE `coach`(
  `coachID` INT(5) NOT NULL ,
  `dateBeganCoaching` DATE NOT NULL ,
  `personID` INT(5) NOT NULL ,
  PRIMARY KEY (`coachID`, `personID`), 
  FOREIGN KEY (`personID`) 
  REFERENCES `person` (`personID`)
  ON DELETE CASCADE 
  ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "family" table.
--
-- This is a linking table 
-- that describes the many:many 
-- relationship between "parent" 
-- and "player" tables.
DROP TABLE IF EXISTS `family` ;
CREATE TABLE `family` (
  `parentID` INT(5) NOT NULL ,
  `playerID` INT(5) NOT NULL ,
    FOREIGN KEY (`playerID` )
    REFERENCES `player` (`playerID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`parentID`)
    REFERENCES `parent` (`parentID`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- Create the "grade" table.
DROP TABLE IF EXISTS `grade`;
CREATE  TABLE `grade`(
  `gradeID` INT(5) NOT NULL AUTO_INCREMENT ,
  `gradeName` VARCHAR(50) NOT NULL ,
  `minWeight` INT(3) NOT NULL ,
  `maxWeight` INT(3) NOT NULL ,
  `minAge` INT(3) NOT NULL ,
  `maxAge` INT(3) NOT NULL ,
  `ballSize` INT(1) NOT NULL ,
  PRIMARY KEY (`gradeID`) )
ENGINE = InnoDB;


-- Create the "coachQualification" table.
DROP TABLE IF EXISTS `coachQualification` ;

CREATE TABLE `coachQualification` (
  `qualID` INT(5) NOT NULL AUTO_INCREMENT ,
  `qualName` CHAR(5) NOT NULL ,
  `gradeID` INT(5) NOT NULL ,
  PRIMARY KEY (`qualID`) ,
    FOREIGN KEY (`gradeID`)
    REFERENCES `grade` (`gradeID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "homePhone" table.
DROP TABLE IF EXISTS `homePhone` ;
CREATE TABLE `homePhone` (
  `homePhoneID` INT(5) NOT NULL AUTO_INCREMENT ,
  `homeNumber` CHAR(9) NOT NULL ,
  PRIMARY KEY (`homePhoneID`))
ENGINE = InnoDB;


-- Create the "mobilePhone" table.
DROP TABLE IF EXISTS `mobilePhone` ;

CREATE TABLE `mobilePhone` (
  `mobilePhoneID` INT(5) NOT NULL AUTO_INCREMENT ,
  `mobileNumber` CHAR(10) NULL DEFAULT NULL ,
  PRIMARY KEY (`mobilePhoneID`))
ENGINE = InnoDB;


-- Create the "emailAddress" table.
DROP TABLE IF EXISTS `emailAddress` ;

CREATE TABLE `emailAddress` (
  `emailAddressID` INT(5) NOT NULL AUTO_INCREMENT ,
  `emailAddress` CHAR(10) NULL DEFAULT NULL ,
  PRIMARY KEY (`emailAddressID`))
ENGINE = InnoDB;


-- Create the "Contact" table
--
-- This is a linking table 
-- that describes the many:many 
-- relationships between "person" 
-- and the "homePhone", "mobilePhone", 
-- and "emailAddress" tables.
DROP TABLE IF EXISTS `contact` ;
CREATE TABLE `contact` (
  `personID` INT(5) NOT NULL ,
  `homePhoneID` INT(5) NOT NULL ,
  `mobilePhoneID` INT(5) NULL DEFAULT NULL ,
  `emailAddressID` INT(5) NULL DEFAULT NULL ,
    FOREIGN KEY (`personID` )
    REFERENCES `person` (`personID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`homePhoneID`)
    REFERENCES `homePhone` (`homePhoneID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`mobilePhoneID`)
    REFERENCES `mobilePhone` (`mobilePhoneID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`emailAddressID`)
    REFERENCES `emailAddress` (`emailAddressID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "qualificationSet" table.
--
-- This is a linking table 
-- that describes the many:many 
-- relationship between "coach" 
-- and "coachQualification" tables.
DROP TABLE IF EXISTS `qualificationSet` ;
CREATE TABLE `qualificationSet` (
  `coachID` INT(5) NOT NULL ,
  `qualID` INT(5) NOT NULL ,
    FOREIGN KEY (`coachID`)
    REFERENCES `coach` (`coachID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`qualID`)
    REFERENCES `coachQualification` (`qualID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "team" table.
DROP TABLE IF EXISTS `team` ;
CREATE TABLE `team` (
  `teamID` INT(5) NOT NULL AUTO_INCREMENT ,
  `teamName` VARCHAR(50) NOT NULL ,
  `teamYear` INT(2) NOT NULL ,
  `gradeID` INT(5) NOT NULL ,
  PRIMARY KEY (`teamID`) ,
    FOREIGN KEY (`gradeID`)
    REFERENCES `grade` (`gradeID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "teamAllocation" table
--
-- this is a linking table for a 
-- many:many relationship between
-- team and player tables.
DROP TABLE IF EXISTS `teamAllocation` ;

CREATE TABLE `teamAllocation` (
  `teamID` INT(5) NOT NULL ,
  `playerID` INT(5) NOT NULL ,
    FOREIGN KEY (`teamID` )
    REFERENCES `team` (`teamID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`playerID`)
    REFERENCES `player` (`playerID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;


-- Create the "teamCoachAllocation" table.
--
-- This is a linking table 
-- that describes the many:many 
-- relationship between "coach" 
-- and "team" tables.
DROP TABLE IF EXISTS `teamCoachAllocation` ;
CREATE TABLE `teamCoachAllocation` (
  `coachID` INT(5) NOT NULL ,
  `teamID` INT(5) NOT NULL ,
    FOREIGN KEY (`coachID`)
    REFERENCES `coach` (`coachID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE,
    FOREIGN KEY (`teamID`)
    REFERENCES `team` (`teamID`)
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

从下面的这些链接:

这是我对规范化到 3NF 的理解:

  • 第一范式意味着不允许重复值
  • 第二范式意味着 1NF 和属性依赖于整个主键而不是主键的一部分(如果该表中的值需要以某种方式相互关联并进行比较,我认为这是分区表)。
  • 第三范式表示 2NF 且没有传递值(例如,如果 x = y 和 y = z,x = z)

将理论知识付诸实践对我来说非常困难,尤其是将“实践”转化为工作的、规范化的 MySQL 代码。如果有人能够帮助我完成模型并给我一些关于将模型规范化为 3NF 的指示,我将非常感激。

提前致谢!

4

2 回答 2

1

我认为这不在 3NF 中,在contact桌子周围。如果我错了,这仍然是存储数据的不好方法,可能应该更改。

对不起,如果这有点混乱......

contact由于整个表是主键,因此完全有可能在表中具有以下结构:

+------------+-------------+----------------+-------- -+
| 人物 | homephoneid | 手机ID | 电子邮件ID |
+------------+-------------+----------------+-------- -+
| 1 | 1 | 1 | 1 |
| 1 | 1 | 1 | 2 |
| 1 | 1 | 2 | 3 |
+------------+-------------+----------------+-------- -+

如您所见,两者homephoneid都是mobilephoneid重复的,因此更新表homephone将导致 3 次更新到contact.

我不同意数据模型,因为您需要一个personhomehone没有,只有一个手机。在这种情况下,当创建一个新的时,person您还必须创建一个新的contact和一个新的homephone.

由于contact只是主键且主键值不能为空,因此您还需要创建 amobilephone和 an emailaddress,这意味着person依赖于emailaddress.

As emailaddressisdependent on contact,这又取决于person你创建了一个循环依赖,它打破了 3NF。

在我看来,如果您想确保人们必须拥有家庭电话号码,您有两种选择:

  1. 如果您只想person拥有一个 homephone,请将其添加到person表中。这是唯一的联系级别信息,应该存储在那里。
  2. 如果你想让人们拥有多个家庭电话号码——记住多个人可以使用同一个电话号码——但不关心手机,那么你需要创建一个表 personhomephones,比如说,用主键personid, homephoneid而不是homephoneid放在联系表。

就个人而言,我不会做这些。我不会确保某人必须有一个家庭电话号码,而是一个主要电话号码,你不在乎它是什么类型。我会允许人们添加不同的联系方式,但不允许这些方式存在

这将需要以下结构:

  • person- 添加primaryPhoneID
  • primaryphone ( primaryphoneID, phonenumber)- PKprimaryphoneID

那么对于允许不存在的联系方式:

  • contactType ( contactTypeID, contactType )- PKcontactTypeID
  • contact ( contactID, contactTypeID, value )- PKcontactID, contactTypeID
  • personContact ( personID, contactID, contactTypeID )- PK一切

虽然这可能会导致重复contactprimaryphone它们是不同的数据位,我认为这很好。如果您坚持不允许任何重复,则必须将电话与其他联系方式分开,这会使模型更加复杂:

  • phonetype ( phoneTypeId, phoneType )
  • phone ( phoneID, phoneTypeID, phonenumber)- PKphoneID, phoneTypeID
  • contactPhone ( personID, phoneTypeID, phoneID )- PK一切
于 2012-04-22T11:17:31.713 回答
1

有一种算法可以最终分解模式中的每个关系,以便在 3NF 中获得等效的模式。谷歌对此有好处!!

要获得有关架构设计的提示,您至少应该描述您需要表示的实体的上下文和功能约束。

于 2012-04-22T09:59:07.257 回答