【问题标题】:Restriction on Rows in Database Tables数据库表中的行限制
【发布时间】:2011-10-18 10:08:39
【问题描述】:

我有三张桌子:

BallId  Color

ball_1  red
ball_2  red
ball_3  blue
ball_4  green
ball_5  green

.......

.

BoxId  Color

box_1  green
box_2  green
box_3  red
.......

.

BoxId  BallId

box_1  ball4
box_1  ball5
box_3  ball2

我想在 BoxId,BallId 表上强制颜色关系,可以示意吗?

【问题讨论】:

  • 我将其解释为:如果前两个表中引用的行具有相同的颜色值,您能否应用约束以使行只能存在于第三个表中?
  • @Hammerite 是的,您的解释完全正确。

标签: mysql database database-design relational-database


【解决方案1】:

我不太确定你的最终目标是什么。

如果您只是想确保底部表中的 BoxIdBallId 必须存在于顶部两个表中,那么您可以使用 FOREIGN KEY(也称为“参照完整性”)。

--- 编辑---

基于其他 cmets/responses,我看到您实际上希望确保通过第三个表连接的 2 行始终具有相同的颜色,但断开连接的行仍然可以具有自己的颜色。

如果是这样,那么您可以像这样“滥用”密钥:

Ball:
    BallId PK, AK1
    Color  AK1

Box:
    BoxId  PK, AK1
    Color  AK1

BallInBox
    BallId PK
    BoxId  PK
    Color
    FK (BallId, Color) references Ball
    FK (BoxId, Color) references Box

这是实际的 DDL SQL:

delimiter $$

CREATE TABLE Ball (
  BallId varchar(45) NOT NULL,
  Color varchar(45),
  PRIMARY KEY (BallId),
  UNIQUE KEY Ball_AK1 (BallId, Color)
)$$

CREATE TABLE Box (
  BoxId varchar(45) NOT NULL,
  Color varchar(45),
  PRIMARY KEY (BoxId),
  UNIQUE KEY Box_AK1 (BoxId, Color)
)$$

CREATE TABLE BallInBox (
  BallId varchar(45) NOT NULL,
  BoxId varchar(45) NOT NULL,
  Color varchar(45),
  PRIMARY KEY (BallId, BoxId),
  CONSTRAINT BallInBox_FK1 FOREIGN KEY (BallId, Color) REFERENCES Ball (BallId, Color),
  CONSTRAINT BallInBox_FK2 FOREIGN KEY (BoxId, Color) REFERENCES Box (BoxId, Color)
)$$

顺便说一句,这允许在“基本”表和“连接”表中使用 NULL 颜色。如果这不是您想要的,添加 NOT NULL 约束很容易。

【讨论】:

  • 不,我想在第三个表中保留 ball-color 和 box-color 的关系。
  • +1 这是一个不错的选择,在 BallInBox 表中有额外的 Color 列。
【解决方案2】:

一种方法(虽然它不是严格的“示意图”)是在第三个表上设置一个插入触发器,它检查正在输入的球/盒子的颜色,如果它们不一样则抛出异常

【讨论】:

    【解决方案3】:

    可以通过数据库使用复合外键来做到这一点:

    create table ball
    (id int unsigned not null primary key auto_increment,
    color varchar(10)) engine=InnoDB;
    
    create table box
    (id int unsigned not null primary key auto_increment,
    color varchar(10)) engine=InnoDB;
    
    create table boxBallRule    
    (ballId int unsigned not null,
    boxId int unsigned not null,
    PRIMARY KEY (ballId,boxId),
    CONSTRAINT `boxBallRule_box_fk1` FOREIGN KEY (boxId) references `box` (id),
    CONSTRAINT `boxBallRule_ball_fk1` FOREIGN KEY (ballId) references `ball` (id)
    ) engine=InnoDB;
    
    create table boxBall
    (id int unsigned primary key auto_increment not null,
    ballId int unsigned not null,
    boxId int unsigned not null,
    CONSTRAINT `boxBallColorRule_fk1` FOREIGN KEY (ballId,boxId) references boxBallRule(ballId,boxId)
    ) engine=InnoDB;
    

    然后,您可以在boxBallRule 表中的哪个框中存储允许哪些球。任何插入到boxBall 表中的不符合“允许”框与球关系的操作都将失败。因此:

    insert into ball (color) values ('red');
    insert into ball (color) values ('blue');
    insert into ball (color) values ('green');
    
    insert into box (color) values ('red');
    insert into box (color) values ('blue');
    insert into box (color) values ('green');
    
    insert into boxBallRule (ballId,boxId) values ((select id from ball where color = 'red'),(select id from box where color = 'red'));
    insert into boxBallRule (ballId,boxId) values ((select id from ball where color = 'blue'),(select id from box where color = 'blue'));
    insert into boxBallRule (ballId,boxId) values ((select id from ball where color = 'green'),(select id from box where color = 'green'));
    
    -- Let's try and put a red ball in a green box. 
    -- The DB should not allow us to do this!
    insert into boxBall (ballId,boxId) values 
    ((select id from ball where color = 'red'),
     (select id from box where color = 'green'));
    

    最后一条语句应该失败,因为它违反了boxBallRule 表上的复合外键。

    【讨论】:

    • 有趣的方法,但是你有维护boxBallRule,每次你插入一个球或一个盒子,你应该在boxBallRule中插入新的行。
    • 确实如此。我不确定你的球和盒子是多么静态/动态。另一种选择是在 boxBall 表上设置一个 before 触发器,比较盒子的颜色和您插入 boxBall 表的球的颜色,如果颜色不匹配则抛出错误。那也行。
    【解决方案4】:

    我认为,就关系理论而言,这个问题的答案如下:你真正想说的是,你有一组盒子和一组球,每个球都在一个盒子里。盒子和球各有一种颜色,一个球只能放在一个匹配颜色的盒子里。但是将球的颜色存储在球表中是设计错误。相反,您应该只存储每个球在哪个盒子中,然后您就知道球是什么颜色,因为您可以检查它存储在里面的盒子的颜色(使用连接)。

    所以,不,没有可以指定的约束来强制执行您想要的关系,但那是因为您以错误的方式处理此问题。你不应该在球表中有Color 列。

    编辑:以上假设每个球都必须在一个盒子里。 OP 澄清不是每个球都需要放在一个盒子里。这似乎是一个更难的问题,因为在这种情况下,您不能依靠盒子表来跟踪球的颜色。我可以看到一些不同的解决方案,但没有一个是完美的。

    1. 使用您的原始设计,并接受它没有提供简单的方法来强制执行您所考虑的约束。
    2. 创建一个新表“unboxed_ball”,用于存储不在盒子中的球,并有一个“颜色”列来记录球的颜色。盒子里的球可以在原来的球台上找到;不在盒子里的球可以在这张新桌子上找到。要查询所有已装箱和未装箱的球,您需要执行 UNION。
    3. 将“假盒子”添加到盒子表中,每种颜色一个,该颜色的未装箱球被视为“内部”(尽管盒子并不真正存在)。如果这个“假盒子”没有真正具有的盒子的其他属性,这可能不太实用。

    【讨论】:

    • 你总结的很好,谢谢。是的,我知道设计错误。但情况是这样的:球颜色表来自默认值,我需要维护该信息,球没有在盒子里。我在动态分配球,当一个球不在盒子里时,我怎么知道它的颜色?
    • 我真的看不出来“将球的颜色存储在球表中是一个设计错误”。除了实体的表之外,还应该在哪里存储实体的属性?
    • @Hammerite:是的,有一个 “执行 OP 所考虑的约束的简单方法。” 请参阅 Branko 的答案。
    • 关于你的第一条评论 - 如果每个球都必须在一个盒子里,那么颜色就成为盒子的属性而不是球的属性。因此,如果您将属性的值与球一起存储,那么您将其放在错误的位置 - 它应该在盒子表中,因为它是盒子的属性。
    • 至于第二条评论,我不得不承认我一开始想到了 Branko 的方法,但是当我意识到 [我认为] 颜色正确地是盒子的属性时,我就打消了它,而不是的球。然后当metdos纠正我(球不必在盒子里)时,我不记得了。得分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多