MySQL:标准化内容类型、类别和子类别
MySQL: Normalizing content types, categories, and subcategories
我有以下基本table结构
内容
- id
- type_id (FK 到 ContentType)
- category_id (可空 FK 到类别)
- sub_category_id (可空 FK 到子类别)
ContentType
- id
类别
- 编号
- content_type_id (FK 到 ContentType)
子类别
- id
- category_id (FK 到类别)
内容的类别应与其类型相匹配,这一点很重要。
同样,内容的子类别应与其类别相匹配。
我知道目前这是对不匹配数据的广泛开放。例如,内容记录可能具有其类型不允许的类别和其类别不允许的子类别。
我如何更改 table 结构以防止这种情况发生?还是我应该坚持使用应用程序逻辑?
请求架构
CREATE TABLE `content_type` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
CREATE TABLE `category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`content_type_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_category_content_type` FOREIGN KEY (`content_type_id`) REFERENCES `content_type` (`id`),
)
CREATE TABLE `sub_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_sub_category_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`),
)
CREATE TABLE `content` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type_id` int(10) unsigned NOT NULL,
`category_id` int(10) unsigned DEFAULT NULL,
`sub_category_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_content_type` FOREIGN KEY (`type_id`) REFERENCES `content_type` (`id`),
CONSTRAINT `fk_content_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`)
CONSTRAINT `fk_content_sub_category` FOREIGN KEY (`sub_category_id`) REFERENCES `sub_category` (`id`)
)
在MySQL中您只能添加基本约束。您无法添加条件约束(很想看到)。
或者,您可以在 INSERT/UPDATE 触发之前构建检查。并使用 SIGNAL 取消 INSERT/UPDATE(参见 https://dev.mysql.com/doc/refman/5.5/en/signal.html)
TBH 我认为如果您有“第 3 方”系统 直接 与数据库对话,它可能会成为一个 true 问题。如果没有,您可以在您的应用程序中解决它。逻辑,并为第 3 方访问创建 API。
如果你真的想在 RDBMS 级别解决这个问题,你可以看看 PostgreSQL(通过 CHECK)。
注意:关于您的 table 类别,我会 考虑 添加一个 parent_id 并将其保留在一个 table 中(自我关系)。这将允许嵌套您的类别已通过第二级。
在类别 table 中,为 (id, content_type_id) 创建唯一索引。在子类别 table 中,在 (id, category_id).
上创建唯一索引
然后在内容 table 中,像这样创建 FK:
constraint FK_ContentCategory foreign key( category_id, type_id )
references Category( id, content_type_id )
constraint FK_ContentSubCategory foreign key( sub_category_id, category_id )
references SubCategory( id, category_id )
类别和子类别 table 中的 FK 保持不变。
由于子类别依赖于内容类型的类别,因此使 FK 引用反映相同的依赖链。现在,在内容 table 中输入 type_id 后,类别和子类别值必须属于正确的依赖链。
但是,如果您需要更改内容条目的类型,则必须先将 category_id 和 sub_category_id 更改为 NULL,修改 type_id,然后插入正确的值进入 category_id 和 sub_category_id。为可靠的数据完整性付出的代价很小。
我有以下基本table结构
内容
- id
- type_id (FK 到 ContentType)
- category_id (可空 FK 到类别)
- sub_category_id (可空 FK 到子类别)
ContentType
- id
类别
- 编号
- content_type_id (FK 到 ContentType)
子类别
- id
- category_id (FK 到类别)
内容的类别应与其类型相匹配,这一点很重要。
同样,内容的子类别应与其类别相匹配。
我知道目前这是对不匹配数据的广泛开放。例如,内容记录可能具有其类型不允许的类别和其类别不允许的子类别。
我如何更改 table 结构以防止这种情况发生?还是我应该坚持使用应用程序逻辑?
请求架构
CREATE TABLE `content_type` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
CREATE TABLE `category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`content_type_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_category_content_type` FOREIGN KEY (`content_type_id`) REFERENCES `content_type` (`id`),
)
CREATE TABLE `sub_category` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_sub_category_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`),
)
CREATE TABLE `content` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`type_id` int(10) unsigned NOT NULL,
`category_id` int(10) unsigned DEFAULT NULL,
`sub_category_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_content_type` FOREIGN KEY (`type_id`) REFERENCES `content_type` (`id`),
CONSTRAINT `fk_content_category` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`)
CONSTRAINT `fk_content_sub_category` FOREIGN KEY (`sub_category_id`) REFERENCES `sub_category` (`id`)
)
在MySQL中您只能添加基本约束。您无法添加条件约束(很想看到)。
或者,您可以在 INSERT/UPDATE 触发之前构建检查。并使用 SIGNAL 取消 INSERT/UPDATE(参见 https://dev.mysql.com/doc/refman/5.5/en/signal.html)
TBH 我认为如果您有“第 3 方”系统 直接 与数据库对话,它可能会成为一个 true 问题。如果没有,您可以在您的应用程序中解决它。逻辑,并为第 3 方访问创建 API。
如果你真的想在 RDBMS 级别解决这个问题,你可以看看 PostgreSQL(通过 CHECK)。
注意:关于您的 table 类别,我会 考虑 添加一个 parent_id 并将其保留在一个 table 中(自我关系)。这将允许嵌套您的类别已通过第二级。
在类别 table 中,为 (id, content_type_id) 创建唯一索引。在子类别 table 中,在 (id, category_id).
上创建唯一索引然后在内容 table 中,像这样创建 FK:
constraint FK_ContentCategory foreign key( category_id, type_id )
references Category( id, content_type_id )
constraint FK_ContentSubCategory foreign key( sub_category_id, category_id )
references SubCategory( id, category_id )
类别和子类别 table 中的 FK 保持不变。
由于子类别依赖于内容类型的类别,因此使 FK 引用反映相同的依赖链。现在,在内容 table 中输入 type_id 后,类别和子类别值必须属于正确的依赖链。
但是,如果您需要更改内容条目的类型,则必须先将 category_id 和 sub_category_id 更改为 NULL,修改 type_id,然后插入正确的值进入 category_id 和 sub_category_id。为可靠的数据完整性付出的代价很小。