外键关系和“属于很多”

时间:2010-05-07 18:35:57

标签: sql database foreign-keys yii

编辑 - 根据下面的回答,我将重新审视我的设计。我认为通过如何设置我的业务对象和规则更加聪明,我可以避免这种混乱。谢谢大家的帮助!

-

我有以下型号:

S属于T

T有很多S

A,B,C,D,E(等)各有1 T,因此T应属于A,B,C,D,E(等)中的每一个

首先我设置我的外键,以便在A中,fk_a_t将是At到T(id)的外键,在B中它是fk_b_t等等。在我的UML中看起来很好(使用MySQLWorkBench) ,但是生成yii模型会导致它认为T有很多A,B,C,D(等)对我来说是相反的。

听起来我要么需要A_T,B_T,C_T(等)表,但这会很痛苦,因为有很多表都有这种关系。我还用谷歌搜索了更好的方法是做某种行为,这样A,B,C,D(等)可以表现为T,但我不知道究竟该如何做到这一点(我将继续谷歌更多关于此)

编辑 - 澄清一下,T只能属于A,B或C中的一个,(等)而不是两个A,也不属于A和B(也就是说,它不是多对多) 。我的问题是关于如何在Yii框架模型中描述这种关系 - 例如,(A,B,C,D,...)HAS_ONE T,而T属于(A,B,C,D,.. )。从业务用例来看,这一切都是有道理的,但我不确定我是否在数据库中正确设置,或者如果我这样做,我需要在Yii中使用“行为”来使其理解关系。 @rwmnau我明白你的意思,我希望我的澄清有所帮助。

UML: uml diagram http://www.freeimagehosting.net/uploads/fd8efa2f1d.png

这是DDL(自动生成)。假装有超过3个表引用T.

-- -----------------------------------------------------
-- Table `mydb`.`T`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`T` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`S`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`S` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `thing` VARCHAR(45) NULL ,
  `t` INT NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_S_T` (`id` ASC) ,
  CONSTRAINT `fk_S_T`
    FOREIGN KEY (`id` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`A`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`A` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `T` INT NOT NULL ,
  `stuff` VARCHAR(45) NULL ,
  `bar` VARCHAR(45) NULL ,
  `foo` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_A_T` (`T` ASC) ,
  CONSTRAINT `fk_A_T`
    FOREIGN KEY (`T` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`B`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`B` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `T` INT NOT NULL ,
  `stuff2` VARCHAR(45) NULL ,
  `foobar` VARCHAR(45) NULL ,
  `other` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_A_T` (`T` ASC) ,
  CONSTRAINT `fk_A_T`
    FOREIGN KEY (`T` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`C`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`C` (
  `id` INT NOT NULL AUTO_INCREMENT ,
  `T` INT NOT NULL ,
  `stuff3` VARCHAR(45) NULL ,
  `foobar2` VARCHAR(45) NULL ,
  `other4` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_A_T` (`T` ASC) ,
  CONSTRAINT `fk_A_T`
    FOREIGN KEY (`T` )
    REFERENCES `mydb`.`T` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

4 个答案:

答案 0 :(得分:1)

您的问题部分在于您无法区分与之相关的表格。

此外,如果您只能有一个记录与三个或四个其他表中的任何一个匹配,则这不是正常关系,并且无法使用常规技术建模。触发器可以确保这是真的但是只有id的列阻止它匹配表A中的id和表C中的a的10(违反规则)。

BTW命名列ID通常是维护的不良选择。如果您使用PKs的表名称命名列并使用PK的确切名称来表示FK,则会更清楚。

另一种解决方案是在中间表中为每种类型的id和一个触发器提供一个列,以确保只有其中一个具有值,但如果您需要所有ID,则查询很麻烦。 id和idtype的复合PK可以确保一个类型中没有重复,但是根本没有重复,你需要一个触发器。

答案 1 :(得分:1)

这是一个经常出现的困境,并且没有完美的解决方案恕我直言。

但我建议如下:

合并S和T表。我认为没有真正需要T表。

反转A / B / C表与S(以前称为T)表的关系。我的意思是删除A / B / C侧的FK并在S侧创建可空的FK列。所以现在你的S表有三个额外的可空列:A_ID,B_ID,C_ID。

在S表上创建一个检查约束,确保这些列中只有一个总是有一个值(或者如果允许的话,它们都没有值)。

如果规则只有一个值,您还可以在这三列中创建唯一约束,以确保只有一个S可以与A / B / C相关。

如果不允许任何这些列中的值,则还必须使用检查约束强制执行上述规则。

评论后更新

好的,那么我会忘记颠倒关系,并将FK保持在A / B / C方面。我仍然会使用检查约束强制使用的唯一性,但它需要跨表,并且对于每种SQL的风格可能看起来不同(例如,SQL Server要求UDF在检查约束中跨表)。我仍然认为你可以在T桌上打核。

关于事物的ORM方面,我根本不知道yii,所以不能说出来。但是如果你在数据库级别强制实现这种关系,那么你如何通过代码实现它应该无关紧要,因为数据库负责数据的完整性(它们看起来就像是与ORM的vanilla关系)。但是,如果在运行时违反了检查约束的规则,则可能会出现捕获特定错误的问题。

我还应该提一下,如果有大量(甚至是相当大的)数据进入相关表格,我推荐的方法可能不是最好的,因为您的检查约束必须检查所有20用于执行规则的表。

答案 2 :(得分:0)

如果它是多对多的关系,你只需要一个中间的桌子,并且听起来不是那样的,所以不要担心这些。

您的问题不明确 - T可以超过1 A,超过1 B,依此类推?或者单个T是属于每个A-E,还是没有其他的?它是1对1关系(每个T只有一个AE)和1对多关系(每个AE正好有1个T,但是T可以属于许多A,很多B,和等等)。这有意义吗?

此外,我还要求您提供更多信息,以帮助巩固您的要求。

答案 3 :(得分:0)

几周前我不得不面对类似的情况(不是我自己的数据库,我更喜欢将所有表合并为一个,除非在非常具体的情况下)。
我实现的解决方案是:在“T”模型文件中,我在relations()函数中执行了类似的操作:

'id_1' => array(self::BELONGS_TO, 'A', 'id'),
'id_2' => array(self::BELONGS_TO, 'B', 'id'),
'id_3' => array(self::BELONGS_TO, 'C', 'id'),

我希望这会对你有所帮助 问候。