如何在多个子实体和列表属性之间建立关系?

时间:2016-04-20 14:44:17

标签: mysql database design-patterns database-design physical-design

我目前正在处理的(基于PHP和MySQL)应用程序的模型包含类似于here描述的继承。为了这个问题的目的简化了类结构,如下所示:

enter image description here

要将此映射到数据库,我正在使用Class Table Inheritance设计模式。这是物理数据模型:

enter image description here

最具体的属性实际上是针对每个子类的。但是有一些属性需要在几个类中(但也不是在所有类中) - 否则它们可以在Foo类/表中进行管理。如果它是一个简单的属性,它会导致一些代码重复,但不是一个大问题。但也有一些复杂属性的案例。

例如:FooTypeBazFooTypeBuz应包含Whatever个元素的列表。

通常我会与包含1:n的表格whatever实施此FOREIGN KEY关系。但在这种情况下,我需要多个FOREIGN KEYwhatever(对于foo_type_bazfoo_type_buz,可能还有一些表格。这很脏。

另一种解决方案:类似于表whatever的“外观”表:

enter image description here

看起来更好(对我来说),但我对这个模型仍然不满意。

如何在多个子实体和集合/列表属性之间建立关系?这个问题是否有优雅的解决方案?也许是最佳实践/设计模式?

1 个答案:

答案 0 :(得分:1)

记录关系很容易 - 您可以创建一个表foo_whatever (foo_id PK, whatever_set_id FK)并仅为适当的foo ID插入行。但是,该架构不会对可以与任何集关联的子类型强制执行任何约束,但现有架构也不会强制该子类型是互斥的。可以使用相同的技术强制执行两者。

考虑在所有foo_*表格中加入一个类型指标,例如使用enum('bar', 'baz', 'buz')。这提供了foo中的子类型信息(这比连接3个表以查找匹配更方便)并允许外键约束和检查约束来强制执行独占子类型并限制可以在{{1}中记录的类型}。是的,它涉及一些冗余信息,但它很小,没有更新异常的风险。

使用涉及类型指示符的复合外键约束以及限制每个子类型表的类型指示符值的检查约束应该可以解决问题。这是我建议的架构:

foo_whatever

但是,由于MySQL忽略了检查约束,因此您需要使用触发器来实现相同的目标:

CREATE TABLE `foo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type` enum('bar','baz','buz') NOT NULL,
  PRIMARY KEY (`id`),
  KEY `foo_id` (`id`,`type`)
);

CREATE TABLE `foo_type_bar` (
  `foo_id` int(11) NOT NULL,
  `foo_type` enum('bar','baz','buz') NOT NULL CHECK (foo_type = 'bar'),
  PRIMARY KEY (`foo_id`),
  KEY `foo_bar_fk` (`foo_id`,`foo_type`),
  CONSTRAINT `foo_bar_fk` FOREIGN KEY (`foo_id`, `foo_type`)
  REFERENCES `foo` (`id`, `type`) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE `foo_type_baz` (
  `foo_id` int(11) NOT NULL,
  `foo_type` enum('bar','baz','buz') NOT NULL CHECK (foo_type = 'baz'),
  PRIMARY KEY (`foo_id`),
  KEY `foo_baz_fk` (`foo_id`,`foo_type`),
  CONSTRAINT `foo_baz_fk` FOREIGN KEY (`foo_id`, `foo_type`)
  REFERENCES `foo` (`id`, `type`) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE `foo_type_buz` (
  `foo_id` int(11) NOT NULL,
  `foo_type` enum('bar','baz','buz') NOT NULL CHECK (foo_type = 'buz'),
  PRIMARY KEY (`foo_id`),
  KEY `foo_buz_fk` (`foo_id`,`foo_type`),
  CONSTRAINT `foo_buz_fk` FOREIGN KEY (`foo_id`, `foo_type`)
  REFERENCES `foo` (`id`, `type`) ON DELETE CASCADE ON UPDATE CASCADE
);

CREATE TABLE `foo_whatever` (
  `foo_id` int(11) NOT NULL,
  `foo_type` enum('bar','baz','buz') NOT NULL CHECK (foo_type IN ('baz', 'buz')),
  `whatever_set_id` int(11) NOT NULL,
  PRIMARY KEY (`foo_id`),
  KEY `whatever_foo_fk` (`foo_id`,`foo_type`),
  KEY `whatever_set_fk` (`whatever_set_id`),
  CONSTRAINT `whatever_foo_fk` FOREIGN KEY (`foo_id`, `foo_type`)
  REFERENCES `foo` (`id`, `type`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `whatever_set_fk` FOREIGN KEY (`whatever_set_id`)
  REFERENCES `whatever_set` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
);