14

我正在尝试创建一个关系,其中可能包含四个不同部分中的任何一个,但相同部分的任何集合都应该被视为唯一的。

示例:分配必须有一个指定的公司,可以选择有一个指定的位置、工作组和程序。分配可能没有没有位置的工作组。

假设我们有公司 A、B、C;位置 X、Y、Z;工作组 I、J、K 和程序 1、2、3。

所以有效的关系可能包括 A - X - I - 1 A - Z - 2 B - Y C C - 3 B - Z - K

但无效的关系将包括 A - K(无位置的工作组) Y - K - 1(无公司)

所以,为了创建我的表,我创建了

companyID INT NOT NULL,
FOREIGN KEY companyKEY (companyID) REFERENCES company (companyID),
locationID INT,
FOREIGN KEY locationKEY (locationID) REFERENCES location (locationID),
workgroupID INT,
FOREIGN KEY workgroupKEY (workgroupID) REFERENCES workgroup (workgroupID),
programID INT,
FOREIGN KEY programKEY (programID) REFERENCES program (programID),
UNIQUE KEY companyLocationWorkgroupProgramKEY (companyID, locationID, workgroupID, programID)

我认为这将处理我的所有关系,除了如果有工作组(我认为我可以很高兴地以编程方式或使用触发器来完成)分配位置的必要性之外

但是,当我测试此架构时,它允许我输入以下内容...

INSERT INTO test VALUES (1, null, null, null), (1, null, null, null);

……毫无怨言。我猜 (1, null, null, null) 不等于自身,因为包含了空值。如果是这种情况,有什么办法可以处理这种关系吗?

任何帮助,将不胜感激!

4

5 回答 5

17

这是一个功能(尽管也不是我所期望的)。

该线程建议将您的密钥设置为主键以获得您期望的行为:

这是一个特性 - NULL 值是未定义的值,因此两个 NULL 值不相同。可能有点令人困惑,但当你考虑它时,它是有道理的。

UNIQUE 索引确实确保非 NULL 值是唯一的;您可以指定您的列不接受 NULL 值。

于 2009-01-09T21:32:41.740 回答
3

我能想到在没有额外的触发器/编程的情况下处理这个问题的唯一方法是在每个引用的表中都有一个“以上都不是”值,这样你的测试看起来像

INSERT INTO test VALUES (1, NO_LOCATION, NO_WORKGROUP, NO_PROGRAM),
                        (1, NO_LOCATION, NO_WORKGROUP, NO_PROGRAM)

其中NO_*标识符是您的 ID 列的正确类型/长度。正如您所期望的那样,这将失败。

于 2009-01-09T21:32:25.143 回答
0

在 MySQL NULL != NULL 或任何东西中。所以这就是 UNIQUE 不起作用的地方。您应该为空白使用另一个默认值,例如零

于 2009-01-09T21:35:03.797 回答
0

我认为重要的是要注意,有一种正确的方法可以解释和处理 NULL 值,并且 OP 表现出的行为正是预期的。您可以无视这种行为,并且可以以任何您想要的方式处理您的查询,而不会引起我的反对,但最好“接受”描述某种形式的最佳实践的答案,而不是非标准的个人偏好。

或者,如果您不同意共识最佳实践,您可以不接受任何答案。

这不是一场尽快让答案被接受的竞赛。我认为,审议和合作也应该成为这个过程的一部分。

于 2009-01-10T04:55:23.610 回答
0

我看到这是在 2009 年提出的。但是经常从 MySQL 请求它:https ://bugs.mysql.com/bug.php?id=8173和https://bugs.mysql.com/bug.php?id =17825例如。人们可以点击影响我来尝试获得 MySQL 的关注。

从 MySQL 5.7 开始,我们现在可以使用以下解决方法:

ALTER TABLE test 
ADD generatedLocationID INT AS (ifNull(locationID, 0)) NOT NULL,
ADD generatedWorkgroupID INT AS (ifNull(workgroupID, 0)) NOT NULL,
ADD generatedProgramID INT AS (ifNull(programID, 0)) NOT NULL,
ADD UNIQUE INDEX (companyID, generatedLocationID, generatedWorkgroupID, generatedProgramID);

生成列是虚拟生成列,没有存储空间。当用户插入(或更新)时,唯一索引会导致生成的列的值动态生成,这是一个非常快速的操作。

于 2019-07-12T03:23:51.600 回答