访问:复合唯一索引忽略NULL

时间:2016-09-09 14:19:08

标签: database ms-access indexing database-design ms-access-2010

考虑这个表结构:

CREATE TABLE [TableA]
(
    [PK_ID] int NOT NULL PRIMARY KEY,
    [Name] text NOT NULL,
    [FK_TableB] int NULL,
    [FK_TableC] int NULL,
    [Value] single NULL
)

我想在NameFK_TableBFK_TableC上创建一个唯一索引,以便这3列中的数据保持唯一。遗憾的是,2个FK列是可空的,Access会自动忽略UNIQUE索引中的NULL,从而实现这一目标:

Name      | FK_TableB | FK_TableC
----------+-----------+-----------
Text1     | NULL      | NULL
Text1     | NULL      | NULL

我尊重Access,让自己相信NULL是否是一个可检查的值,但在这种情况下它会产生令人难以置信的反作用。在SQL Server中创建这样的索引非常有效,我很乐意在Access中找到一种方法。

到目前为止,这是我尝试(并且失败)/考虑过的事情:

  • 创建验证规则,通过COUNT函数检查唯一性。

    • 访问:"您将无法在验证规则中使用聚合函数。"
  • 使用NZ函数创建唯一索引,该函数将检查NULL。

    • 访问:"您将无法在索引中使用任何类型的功能。"
  • 创建所需的FK列,并为每个相关表插入一个默认/类似NULL的记录。

    • 维持和不良做法的痛苦。
  • 插入一个连接3列中每一列的附加列,并创建只检查新列的UNIQUE索引。

    • 冗余和不良做法。

我的同事正在考虑为每个可能的案例创建一个表格:

  1. 不可为空的唯一Name
  2. 不可为空的唯一NameFK_TableB
  3. 不可为空的唯一NameFK_TableBFK_TableC
  4. 这是我唯一的解决方案吗?

3 个答案:

答案 0 :(得分:1)

  

创建所需的FK字段,并为每个相关表插入一个默认/类似NULL的记录。

这是我首选的解决方案,使用FK 0

我不知道维护是多么痛苦 - 只需将记录插入相关表中一次,将FK默认值从NULL更改为0,然后完成。

您可能需要更改一些应用程序逻辑(现在测试FK_TableB IS NOT NULL,然后执行FK_TableB > 0)。

恕我直言,这也是不错的做法,它比可以为空的FK好。

它可以帮助您避免执行大量的OUTER JOIN - 它们可能会产生不可编辑的查询结果或性能不佳等问题。

答案 1 :(得分:0)

索引忽略了许多SQL数据库中的 NULL ,以便支持三值逻辑引起的悖论。结果是唯一索引不会以您希望的方式约束数据。

这就是我所说的三重逻辑。当 NULL NULL 测试相等时,结果既不是 TRUE 也不是 FALSE ,而是第三个逻辑值的 UNKNOWN 即可。如果这看起来像一个丑陋的蠕虫,那是因为它是。

您在FK中使用 NULL 不仅无法为引用的链接提供值,而且还断言此实例中不存在可选关系。这是一个合理的解释,但它不是索引构建者通常使用的解释。顺便说一下,几乎任何SQL数据库都会遇到完全相同的问题。

解决方案?那么,您可以将数据规范化为第六范式,这种形式在规范化讨论中经常被忽略。在6NF中,每个可空列导致分解为两个表,其中一个表包含可空列,另一个表不包含。结果是每个列都可以被标记为不可为空,而不会损失表达能力。

这会让你解决你所说的问题,但它可能比它的价值更麻烦。

答案 2 :(得分:0)

有一个无需编码的解决方案。

这是 Jet 引擎中的一个损坏的功能。索引定义中的“忽略空值”标志应该具有这种效果,在我看来这是失败的。我没有检查过的是,我的印象是它在旧版本的 DAO 中做正确的事情。在我的职业生涯中,我曾多次以这种方式(姓名和姓氏 + 重复数据删除)进行索引,其中“重复数据删除”几乎总是空的。

索引不适用于值 Null,但适用于值 Empty ("")。 将第二个字段设置为 'Null forbidden' 和 'Empty string allowed',作为解决方案,将 "" 设置为其默认值。

插入新记录时,第二个字段将设置为 Empty,索引将按预期插入或不插入。字段的更新将留下或返回“空”给第二个字段。