外键关系和“属于多个”关系

发布于 2024-08-31 21:52:33 字数 3201 浏览 2 评论 0 原文

编辑 - 根据下面的回复,我将重新审视我的设计。我认为我可以通过更聪明地制定我的业务目标和规则来避免这种混乱。感谢大家的帮助!

--

我有以下模型:

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 图

这是 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;

edit - Based on the responses below, I'm going to revisit my design. I think I can avoid this mess by being a little bit more clever with how I set out my business objects and rules. Thanks everyone for your help!

--

I have the following model:

S belongs to T

T has many S

A,B,C,D,E (etc) have 1 T each, so the T should belong to each of A,B,C,D,E (etc)

At first I set up my foreign keys so that in A, fk_a_t would be the foreign key on A.t to T(id), in B it'd be fk_b_t, etc. Everything looks fine in my UML (using MySQLWorkBench), but generating the yii models results in it thinking that T has many A,B,C,D (etc) which to me is the reverse.

It sounds to me like either I need to have A_T, B_T, C_T (etc) tables, but this would be a pain as there are a lot of tables that have this relationship. I've also googled that the better way to do this would be some sort of behavior, such that A,B,C,D (etc) can behave as a T, but I'm not clear on exactly how to do this (I will continue to google more on this)

EDIT - to clarify, a T can only belong to one of A, or B, or C, (etc) and not two A's, nor an A and a B (that is, it is not a many to many). My question is in regards to how to describe this relationship in the Yii Framework models - eg, (A,B,C,D,...) HAS_ONE T , and T belongs to (A,B,C,D,...). From a business use case, this all makes sense, but I'm not sure if I have it correctly set up in the database, or if I do, that I need to use a "behavior" in Yii to make it understand the relationship. @rwmnau I understand what you mean, I hope my clarification helps.

UML:
uml diagram

Here's the DDL (auto generated). Just pretend that there is more than 3 tables referencing 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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

方圜几里 2024-09-07 21:52:33

您的问题部分在于您无法区分它与哪个表相关。

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

顺便说一句,命名列 ID 通常对于维护来说是一个糟糕的选择。如果您使用 PK 的表名称来命名列,并使用 FK 的 Pk 的确切名称,则发生的情况会更清楚。

另一种解决方案是在中间表中为每种类型的 id 提供一列,并设置一个触发器以确保只有其中一个具有值,但如果您需要所有 id,那么查询起来会很痛苦。 id 和 idtype 的复合 PK 可以确保类型内不重复,但要完全不重复,您将需要一个触发器。

Your problem is in part that you have no way to distinguish which of the tables it is in relation to.

Further if you can only have one record that matches any of three or four other tables, this is not a normal relationship and cannot be modelled using normal techniques. A trigger can ensure this is true but with only the column of id in it what prevents it from matching an id in table A of 10 and anid in table C of 10 (violating the rules).

BTW naming columns ID is usually a poor choice for maintenance. It is much clearer what is going on if you name the column with table name for PKs and use the exact name of the Pk for FKs.

An alternative solution for you is to have in the middle table a column for each type of id and a trigger to ensure that only one of them has values, but this is a pain to query if you need all the ids. A compound PK of id and idtype could work to ensure no repeats within a type, but to have no repeats at all, you will need a trigger.

星星的軌跡 2024-09-07 21:52:33

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

不过我建议如下:

结合 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 的普通关系)。但是,如果在运行时违反检查约束规则,则可能会出现捕获出现的特定错误的问题。

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

This is a dilemma that comes up fairly regularly, and there is no perfect solution IMHO.

However I would recommend the following:

Combine the S and T table. I don't see any real need for the T table.

Invert the way the A/B/C tables relate to the S (formerly T) table. By this I mean remove the FK on the A/B/C side and create nullable FK columns on the S side. So now your S table has three additional nullable columns: A_ID, B_ID, C_ID.

Create a check constraint on the S table, ensuring that exactly one of these columns always has a value (or none of them has a value if that is allowed).

If having exactly one value is the rule, you can also create a unique constraint across these three columns to ensure that only one S can be related to an A/B/C.

If no value in any of these columns is allowed, the above rule will have to be enforced with a check constraint as well.

Update After Your Comment

Ok, then I would forget about inverting the relationships, and keep the FK on the A/B/C side. I would still enforce the uniqueness of usage using a check constraint, but it would need to cross tables and will likely look different for each flavor of SQL (e.g. SQL Server requires a UDF to go across tables in a check constraint). I still think you can nuke the T table.

Regarding the ORM side of things, I don't know yii at all, so can't speak to that. But if you enforce the relationship at the database level, how you implement it via code shouldn't matter, as the database is responsible for the integrity of the data (they will just look like vanilla relationships to the ORM). However, it may present a problem with trapping the specific error that comes up if at runtime the check constraint's rule is violated.

I should also mention that if there is a large (or even reasonably large) amount of data going into the tables in question, the approach I am recommending might not be the best, as your check constraint will have to check all 20 tables to enforce the rule.

南渊 2024-09-07 21:52:33

如果是多对多关系,则只需要中间有一张表,但听起来并非如此,所以不必担心这些。

你的问题不清楚——一个T可以属于多个A、多个B等等吗?或者单个 T 属于每个 AE,而不属于其他?这是一对一关系(每个 T 恰好有一个 AE)和一对多关系(每个 AE 恰好有 1 个 T,但一个 T 可以属于多个 A、多个 B,并且很快)。这有道理吗?

另外,我会附议您在问题中提供更多信息的请求,以帮助巩固您的要求。

You only require a table in the middle if it's a many-to-many relationship, and it doesn't sound like that's the case, so don't worry about those.

Your question isn't clear - can a T belong to more than 1 A, more than 1 B, and so on? Or does a single T belong to each of A-E, and to no others? It's the difference between a 1-to-1 relationship (each T has exactly one each of A-E) and a 1-to-many relationship (each A-E has exactly 1 T, but a T can belong to many A, many B, and so on). Does this make sense?

Also, I'd second the request for some more info in your question to help solidify what you're asking for.

烟燃烟灭 2024-09-07 21:52:33

几周前我必须面对类似的情况(不是我自己的数据库,我更喜欢将所有表合并为一个,除非在非常特殊的情况下)。
我实现的解决方案是:在“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'),

我希望这对您有帮助。
问候。

I have to face a similar situation some weeks ago (not my own db, I prefer to combine all the tables into one, unless in very specific situations).
The solution I implemented was: In "T" model file I did something like this at relations() function:

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

I hope this helps you.
Regards.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文