假设我有一个名为ENUM
的{{1}}列和一个名为Category
的{{1}}列。我有时会单独ENUM
Subcategory
,这就是为什么它们会分开。
SELECT
但并非所有子类别对所有类别都有效(例如,Category
仅对CREATE TABLE `Bonza` (
`EventId` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`Category` ENUM("a", "b", "c") NOT NULL,
`Subcategory` ENUM("x", "y", "z") NOT NULL,
PRIMARY KEY(`EventId`)
) ENGINE=InnoDB;
和"z"
有效),并且让我觉得这个约束没有被烘焙进入表的设计。如果MySQL有某种"对" type(该类型的列可以在值的前导子序列上转换)然后这不会是这样的问题。
如果我想在类别和子类别之间保持完整性,我会坚持在触发器中编写长条件。或者我离开它会更好吗?你会做什么?
我认为最接近面向关系的方法是存储"a"
,并将其映射到包含所有有效事件类型对的表,并在每次我想查找其含义时加入该表。一个事件类别。
"b"
我能做更简单的事吗?这次查询可能会让我在调用代码时花费复杂性和性能,EventCategoryId
转换为CREATE TABLE `Bonza` (
`EventId` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`EventCategoryId` INT UNSIGNED NOT NULL,
PRIMARY KEY(`EventId`),
FOREIGN KEY `EventCategoryId` REFEFRENCES(`EventCategories`.`EventCategoryId`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE `EventCategories` (
`EventCategoryId` INT UNSIGNED NOT NULL,
`Category` ENUM("a", "b", "c") NOT NULL,
`Subcategory` ENUM("x", "y", "z") NOT NULL,
PRIMARY KEY(`EventCategoryId`)
) ENGINE=InnoDB;
-- Now populate this table with valid category/subcategory pairs at installation
,不是吗?
答案 0 :(得分:1)
假设您的类别和子类别经常不会改变,并且假设您愿意接受大更新,那么您可以执行以下操作:
使用EventCategories
表来控制类别和子类别之间的层次约束。该表的主键应该是包含Category
和Subcategory
的复合键。在Bonza
表中引用此表。 Bonza
中的外键恰好包含您要过滤的两个列,因此您无需加入即可获取您之后的内容。也无法分配无效的组合。
CREATE TABLE `Bonza` (
`EventId` UNSIGNED INT NOT NULL AUTO_INCREMENT,
`Category` CHAR(1) NOT NULL,
`Subcategory` CHAR(1) NOT NULL,
PRIMARY KEY(`EventId`),
FOREIGN KEY `Category`, `Subcategory`
REFEFRENCES(`EventCategories`.`Category`, `EventCategories`.`Subcategory`)
ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE `EventCategories` (
`EventCategoryId` UNSIGNED INT NOT NULL,
`Category` CHAR(1) NOT NULL,
`Subcategory` CHAR(1) NOT NULL,
PRIMARY KEY(`Category`, `Subcategory`)
) ENGINE=InnoDB;
答案 1 :(得分:1)
我的想法是:“最好”几乎总是以意见为基础,但仍有一些常见的事情可以说
如果您遇到的问题并非所有配对都有效 - 您就会遇到问题 - 您必须存储此信息。因此,您需要存储哪些对无效或存储哪些对是有效的。根据关系DBMS,包含附加表的示例完全有效。事实上,如果我们将面临这样的问题,它几乎是在数据库设计层面上解决它的唯一方法。有了它:
FOREIGN KEY
维护参照完整性。因此,您的数据将始终正确并指向有效的对可能会发生什么不好的事情,这会对性能产生怎样的影响?
要重建完整行,您需要使用简单的JOIN
:
SELECT
Bonza.id,
EventCategories.Subcategory,
EventCategories.Category
FROM
Bonza
LEFT JOIN EventCategories
ON Bonza.EventCategoryId=EventCategory.id
JOIN
的效果会很好:你会做FK - 因此,根据定义,你只会获得INDEX SCAN
。这是关于索引质量(即它的基数) - 但总的来说它会很快。JOIN
有多复杂?它操作简单 - 但它可能会给复杂查询增加一些开销。但是,在我看来:没关系。 中没有任何复杂的。EventCategories
数据来更改配对。也就是说:您可以轻松删除对禁止对的限制,这将影响 nothing 。我认为这是这种结构的一大好处。但是,添加新限制并非如此简单 - 因为,是的,它需要DELETE
操作。您已为您的FK选择ON DELETE RESTRICT
操作 - 这意味着您必须在添加新限制之前处理所有冲突记录。当然,这取决于你的应用程序的逻辑 - 但是以另一种方式考虑它:如果你要添加新的限制,那么就不应该删除所有冲突的记录(因为逻辑说:是的,它们应该)?如果是,请将您的FK更改为ON DELETE CASCADE
。所以:拥有额外的表格是简单,灵活并且实际上简单方式来解决您的问题。
您已经提到,您可以使用触发器来解决您的问题。这实际上是适用的,所以我会表明 - 这有它的弱点(好吧,还有一些好处)。让我们说,我们将创建触发器:
DELIMITER //
CREATE TRIGGER catCheck BEFORE INSERT ON Bonza
FOR EACH ROW
BEGIN
IF NEW.Subcategory = "z" && NEW.Category = "c" THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid category pair';
END IF;
END;//
DELIMITER ;
显然,我们仍然 来存储有关如何验证我们的对的信息,但在这种情况下,我们存储无效的组合。一旦我们得到无效数据,我们将捕获这个内部触发器并中止我们的插入,返回正确的用户定义的errno(45000
)和一些解释文本。现在,复杂性和性能如何?
JOIN
- 完整性由另一个工具维护。您可能会忘记存储对并处理它们,将此逻辑隐藏在触发器中SELECT
语句中获胜:您的数据始终包含有效对。并且不需要JOIN
INSERT
/ UPDATE
语句中松散:它们将调用触发器并在其中 - 一些检查条件。它可能很复杂(许多IF
部分),MySQL将逐一检查它们。制定一个单一的条件并没有多大帮助 - 因为在最坏的情况下,MySQL会检查它直到它结束。 JOIN
案例不同,您将无法执行任何级联操作。相反,你必须进行手动处理。对于一般情况,如果您不确定 - 您的申请条件是什么,我建议您使用JOIN
选项。它简单,易读,可扩展。它符合关系DB原则。
对于某些特殊情况,您可能想要选择第二个选项。那些条件是:
SELECT
语句将更频繁地完成,然后INSERT
/ UPDATE
语句。此外,SELECT
语句性能在应用程序性能方面也是最高优先级。 答案 2 :(得分:0)
我喜欢这个问题但是,有了这些信息,我会为一个枚举列定义一组有效的对:
CategorySubcategory ENUM("ax", "ay", "az", "bx", "by", "bz", "cx", "cy")
我认为这只会对一组有限的值有用,当他们个人变大时我会选择你的第二个选项而不是触发的选项。 第一个原因绝对是一种观点,我不太喜欢触发器,而且他们不喜欢我 第二个原因是从一个表到另一个表的良好索引和适当大小的引用具有非常高的性能