【问题标题】:Table with 2 foreign keys coming up from one primary具有来自一个主的 2 个外键的表
【发布时间】:2014-05-25 20:57:41
【问题描述】:

我正在为我的一个学校项目制作这个包含超级英雄的数据库,并且我有一个超级英雄表(显然)和一个敌人表。所以敌人表有两个外键:

bad_superhero_id

good_superhero_id

此表的目的是将角色表(超级英雄)中的好超级英雄与坏超级英雄(他们的敌人)联系起来。两个外键都从 superheroes 表的 id 中获取值。问题是我的老师不喜欢这样,我不知道为什么。我的意思是,我在一本名为 Beginning PHP5, Apache, and MySQL Web Development 的书中看到了这个例子,我还询问了在创建数据库结构方面有良好经验的同事。他们说这不是问题,但我的老师想让我举个例子,因为她认为这不是很好的关系,并希望我创造一个她想到的愚蠢的解决方法。我仍然认为这不是建立这种关系的好方法,所以我想在这里询问关于这个问题的第三意见。如果您提出您的意见,我将不胜感激,以便我了解使用这样的关系是坏、好还是无关紧要的做法。

编辑:

CREATE TABLE superhero (
    id INT NOT NULL AUTO_INCREMENT,
    nick_name VARCHAR,
    align ENUM ('good', 'bad'),
    PRIMARY KEY(id)
)   ENGINE=INNODB;

CREATE TABLE enemies_link (
    id INT NOT NULL AUTO_INCREMENT,
    good_sh_id INT NOT NULL,
    bad_sh_id INT NOT NULL,
    PRIMARY KEY (id),

    FOREIGN KEY (good_sh_id, bad_sh_id)
      REFERENCES superheroes(id)
      ON UPDATE CASCADE ON DELETE RESTRICT
)   ENGINE=INNODB;

EDIT2: 是的,我忘了补充说我想要 n 到 n 连接。假设蜘蛛侠的敌人有毒液和绿妖精,另一方面,毒液有一些其他优秀的超级英雄作为敌人等等。

【问题讨论】:

  • 请编辑您的帖子并包含两个表格的定义。谢谢。
  • 我不完全确定您的目标是什么。请包括表定义
  • 这个表定义了什么样的关系?另外,您的老师提倡的替代方法是什么?
  • 添加了一些修改。在这种情况下,我老师的方法并不重要。我只是感兴趣这是正确的做法还是不好的做法
  • 等等:你是把enemies_link定义为一个新表还是一个复合外键?

标签: mysql sql database foreign-keys


【解决方案1】:

您的老师可能是对的:您很可能应该将超级英雄和敌人 id 定义为单独的外键:

FOREIGN KEY good_sh_id REFERENCES superheroes(id),
FOREIGN KEY bad_sh_id REFERENCES superheroes(id)

您指定的语法会将超级英雄引用指定为a composite foreign key。我不得不承认我不确定这甚至意味着什么。复合外键对我有意义的唯一方法是当您使用它们来引用复合主键时。

【讨论】:

  • 是的,我忘记补充说我想要 n 到 n 连接。假设蜘蛛侠的敌人有毒液和绿妖精,另一方面,毒液有一些其他优秀的超级英雄作为敌人。
【解决方案2】:

您的设计本质上并不是一个糟糕的设计,但它需要改进。您正在使用定义 n 对 n 关系的交叉/交叉表。这些在生产数据库中一直使用,例如学生和课程之间的关系,其中一个学生可以参加几门课程,一个课程将有很多学生注册。你的只是指双方同一张桌子。那也很好。例如,一个零件表可以包含组件和模块,其中一个组件用于制作许多模块,而一个模块由许多组件组成。

在您的特定情况下,您有一个标志来指定超级英雄是坏还是好。这很好(尽管“坏英雄”的概念有些令人震惊——“超级存在”不是更好的名称吗?),但标志和 id 必须在唯一的约束/索引中一起定义。这似乎是多余的,因为 id 是主键,因此它本身就是唯一的。但是外键只能引用一个唯一的字段或一组字段。

至于交叉表,你真的不需要单独的 id 字段。事实上,这在数据完整性方面打开了一个可能的裂缝。建模时,始终尝试将数据完整性作为主要因素。尽可能将虚假数据放入表中。表键都将是外键字段作为一个大的复合键。如果 foolish 设计标准需要单独的键,那么一定要在唯一索引中一起定义外键字段。然后您必须强制执行好/坏标志的值,以确保“好”FK 只能指向“好”超级英雄,依此类推。

CREATE TABLE superhero(
    id INT NOT NULL AUTO_INCREMENT,
    nick_name VARCHAR( 20 ),
    align ENUM( 'good', 'bad' ) not null default 'good',

    PRIMARY KEY( id ),
    constraint unique id_align_uq( id, align )
)   ENGINE=INNODB;

CREATE TABLE enemies_link(
  good_sh_id INT NOT NULL,
  good_align enum( 'good', 'bad' ) not null check( good_align = 'good' ),
  bad_sh_id INT NOT NULL,
  bad_align enum( 'good', 'bad' ) not null check( bad_align = 'bad' ),

  PRIMARY KEY( good_sh_id, good_align, bad_sh_id, bad_align ),

  FOREIGN KEY( good_sh_id, good_align )
    REFERENCES superhero( id, align )
    ON UPDATE CASCADE ON DELETE RESTRICT,

  FOREIGN KEY( bad_sh_id, bad_align )
    REFERENCES superhero( id, align )
    ON UPDATE CASCADE ON DELETE RESTRICT
)   ENGINE=INNODB;

【讨论】:

  • 非常感谢您的回答。我只是不明白为什么第二张桌子上有 4 个主键?
  • 不是4个主键,是4个字段组成的一个主键。它确保没有两排具有相同的好超级英雄与同一个坏超级英雄相关联。多次允许相同的关系是没有意义的,而且是设计缺陷。
【解决方案3】:

我认为你有正确的方法。我用这个例子澄清并可能重复你已经设计的东西。

table hero (in other words person)
person_id, name, good_bad

(顺便说一句,good_bad 可能会有所不同,因此请考虑是否在正确的位置)

table  opponent
person_id, opponent_person_id, battlefield

所以你可以在不同的战场上拥有不同的对手 您唯一的问题是确保重复输入或如何处理此问题的概念:例如;

person_id = 7, opponent_person_id = 11, battlefield = Rome
person_id = 11, opponent_person_id =7, battlefield = Rome

在商业中这可能是一个现实的用途:

table  employment
chief_person_id,employe_person_id, department
chief_person_id = 7, employe_person_id = 10, department= 1
chief_person_id = 7, employe_person_id = 11, department= 1
chief_person_id = 9, employe_person_id = 12, department= 2
chief_person_id = 9, employe_person_id = 15, department= 2
chief_person_id = 12, employe_person_id = 16, department= 2 (even  sub-hierarchies can be shown. see id=12)

【讨论】:

    猜你喜欢
    • 2010-10-09
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    • 2016-08-15
    相关资源
    最近更新 更多