多列的唯一性约束会导致null值出现问题

时间:2017-08-16 15:09:30

标签: oracle

根据oracle文档,null不能等于或等于任何值或不等于null

在任何列的唯一性约束的情况下都很明显。但是如果多列上的唯一性约束,则行为是不同的。例如:

CREATE TABLE table1 (
    col1 NUMBER(2),
    col2 NUMBER(2),
    CONSTRAINT uniq_col1_col2 UNIQUE (col1, col2)
);
INSERT INTO table1 VALUES (1, NULL);
INSERT INTO table1 VALUES (1, NULL);
# ORA-00001: unique constraint (XYZ.UNIQ_COL1_COL2) violated

为什么会这样?如何指定约束以忽略空值?

修改

更具体地说,如果行(null), (null)是唯一的,为什么(1,null), (1,null)不唯一?这背后的理由是什么?

2 个答案:

答案 0 :(得分:5)

那是the documentation says(强调添加):

  

为了满足唯一约束,表中没有两行可以具有唯一键的相同值。但是,由单个列组成的唯一键可以包含空值。要满足复合唯一键,表或视图中的任何两行都不能在键列中具有相同的值组合。所有键列中包含空值的任何行都会自动满足约束。 但是,包含一个或多个键列的空值以及其他键列的相同值组合的两行违反了约束。

它正在做他们应该做的事情。使用两个示例插入时,两个(可能的)行在一个键列中包含null,在另一个键列中包含相同的值(1),因此违反了约束。

没有别的东西真的有意义;无论如何,允许两个插入前进都会给你留下两个难以区分的行,关键是。

你问:

  

更具体地说,如果行(null), (null)是唯一的,为什么(1,null), (1,null)不唯一?这背后的理由是什么?

因为没有其他关键列可以强制执行唯一性。

如你所说,null不等于或不等于任何东西。如果您的唯一密钥仅在col1上,并且您有两行设置为null,这是允许的,那么查询where col1 is null会找到两者 - 这是正常的,因为is null不是&#39关于平等。您可以说两个行都匹配条件,但不是它们等于null。使用双列密钥,等效值为where col1 = 1 and col2 is null。现在平等确实发挥作用。

在这两种情况下都会忽略空值,剩下的任何内容仍然必须是唯一的。使用单列密钥时,没有其他任何东西可以强制执行唯一性。如果col2为空,则使用双列密钥,然后col1仍然需要进行比较,并且它本身必须是唯一的,有效的。

您也可以这样做:

INSERT INTO table1 VALUES (null, null);
INSERT INTO table1 VALUES (null, null);

同样的事情适用;它们的空值被有效忽略,但现在 - 与单列密钥一样 - 没有任何东西可以强制执行唯一性。

答案 1 :(得分:1)

如果你真的想要一个忽略空值的约束并且只阻止插入完整的重复键,那么你可以使用一个只保存完全非空的键的基于函数的唯一索引:

create unique index uniq_col1_col2 on table1
  ( case when col1 is not null and col2 is not null then col1 end
  , case when col1 is not null and col2 is not null then col2 end
  );