MySQL 问题 - 唯一键无法正常工作,或者我误解了?

发布于 2024-07-11 03:29:48 字数 1120 浏览 6 评论 0原文

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

例子: 任务必须有指定的公司,可以选择指定地点、工作组和计划。 分配可能没有没有位置的工作组。

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

因此有效关系可以包括 A-X-I-1 A-Z-2 经过 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) 不等于自身,因为包含了 null。 如果是这样的话,我有什么办法可以处理这种关系吗?

任何帮助,将不胜感激!

I'm trying to create a relation where any of four different parts may be included, but any collection of the same parts should be handled as unique.

Example:
An assignment must have an assigned company, may optionally have an assigned location, workgroup and program.
An assignment may not have a workgroup without a location.

Let's assume we have companies A, B, C; locations X, Y, Z; workgroups I, J, K and programs 1, 2, 3.

So valid relations could include
A - X - I - 1
A - Z - 2
B - Y
C
C - 3
B - Z - K

But invalid relations would include
A - K (Workgroup without location)
Y - K - 1 (No company)

So, to create my table, I've created

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)

I figure this would handle all my relations besides the neccessity of an assignment to have a location if there is a workgroup (which I can happily do programatically or with triggers, I think)

However, when I test this schema, it allows me to enter the following...

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

...without complaint. I'm guessing that (1, null, null, null) does not equal itself because nulls are included. If this is the case, is there any way I can handle this relation?

Any help would be appreciated!

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

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

发布评论

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

评论(5

只有一腔孤勇 2024-07-18 03:29:48

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

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

这是一个功能 - NULL 值是
未定义值,因此两个 NULL
值不一样。 可以是一个
有点令人困惑,但当
你考虑一下。

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

This is a Feature (though not what I expected, either).

This thread suggests making your key a Primary key to get the behavior you expected:

This is a feature - a NULL value is an
undefined value, therefore two NULL
values are not the same. Can be a
little confusing but makes sense when
you think about it.

A UNIQUE index does ensure that
non-NULL values are unique; you could
specify that your column not accept
NULL values.

踏月而来 2024-07-18 03:29:48

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

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

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

The only way I can think of handling this without additional triggers/programming would be to have a single "None of the Above" value in each of the referenced tables, so that your test would look like

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

Where the NO_* identifiers are the right type/length for your ID columns. This would then fail, as you'd expect it.

绝情姑娘 2024-07-18 03:29:48

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

In MySQL NULL != NULL, or anything. So that is what the UNIQUE doesn't work. You should use another default value for blanks, like zero

爱殇璃 2024-07-18 03:29:48

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

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

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

I think it's important to note that there is a proper way for NULL values to be interpreted and handled, and the behavior exhibited by the OP is exactly what's intended. You can disregard that behavior, and you can handle your query any way you want without objection from me, but it might be well to "Accept" an answer that describes some form of Best Practices, rather than a non-standard personal preference.

Or if you don't agree with the consensus Best Practice, you can just not Accept any answer.

It's not a race to get an answer accepted as quickly as possible. Deliberation and collaboration are also intended to be part of the process, I think.

对风讲故事 2024-07-18 03:29:48

我看到这个问题是在 2009 年提出的。但是 MySQL 经常会提出这样的要求: https:// /bugs.mysql.com/bug.php?id=8173https例如:://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);

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

I see that this was asked in 2009. However it is often requested from MySQL: https://bugs.mysql.com/bug.php?id=8173 and https://bugs.mysql.com/bug.php?id=17825 for example. People can click on affects me to try and get attention from MySQL.

Since MySQL 5.7 we can now use the following workaround:

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);

The generated columns are virtual generated columns, so they have no storage space. When a user inserts (or updates) then the unique index cause the value of the generated columns to be generated on the fly which is a very quick operation.

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