我正在用我的学校项目的超级英雄制作这个数据库,我有一个超级英雄表(显然)和一个敌人表。所以敌人表有两个外键:
bad_superhero_id
和
good_superhero_id
此表的目的是将好的超级英雄与角色表(超级英雄)中的坏超级英雄(他们的敌人)联系起来。两个外键都从超级英雄表的id中获取值。问题是我的老师不喜欢这个,我不知道为什么。我的意思是,我在一本名为 开始PHP5,Apache和MySQL Web开发 的书中看到了这个例子,我还问过我的同事在创建数据库结构方面有很好的经验。他们说这不是问题,但是我的老师想让我给她一个使用它的例子,因为她不认为这是一个很好的关系,并希望我创建一个她想到的愚蠢的解决方法。我仍然认为创建这种关系并不是一种糟糕的方式,所以我想在这里要求就此问题获得第三意见。如果你发表意见以便我能理解使用这样的关系是不好的,好的或无关紧要的,我将不胜感激。
编辑:
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的连接。让我们说蜘蛛侠为他的敌人拥有毒液和绿色精灵,另一方面毒液还有其他一些好的超级英雄作为敌人等等。
答案 0 :(得分:2)
你的老师可能是对的:你很可能应该将超级英雄和敌人的ids定义为单独的外键:
FOREIGN KEY good_sh_id REFERENCES superheroes(id),
FOREIGN KEY bad_sh_id REFERENCES superheroes(id)
您指定的语法会将超级英雄引用指定为a composite foreign key。我不得不承认,我不确定这甚至意味着什么。复合外键对我有意义的唯一方法就是当你使用它们来引用复合主键时。
答案 1 :(得分:1)
我认为你有正确的方法。我清楚并且可能重复你已经设计过的例子。
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)
答案 2 :(得分:1)
您的设计本质上不是一个糟糕的设计,但它需要工作。您正在使用定义n对n关系的交叉/交叉表。这些在生产数据库中一直使用,例如学生和课程之间的关系,学生可以参加几门课程,课程将有许多学生注册。你的只是将两边都指向同一张桌子。那太好了。例如,零件表可以包含组件和模块,其中包含用于制作许多模块的组件和由许多组件组成的模块。
在您的特定实例中,您有一个标志,指示超级英雄是坏还是好。这很好(虽然"坏英雄的概念"有点震撼 - 不会超级"超级"是一个更好的名称?),但旗帜和id必须在唯一约束/索引中一起定义。这似乎是多余的,因为id是主键,因此它本身就是唯一的。但是外键只能引用一个唯一的字段或一组字段。
至于交叉表,你真的不需要一个单独的id字段。实际上,这开启了数据完整性的可能缺口。建模时,始终尽量使数据完整性成为主要因素。尽可能接近不可能将伪造的数据放入表中。表键将所有外键字段作为一个大的复合键。如果愚蠢设计标准需要单独的密钥,那么请确保将外键字段一起定义在唯一索引中。然后你必须强制执行好/坏旗帜的价值,以确保“好”和“好”。 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;