Oracle:创建唯一索引但忽略NULL

时间:2012-08-09 22:50:42

标签: oracle11g

我在SO上看到了这个例子,它给出了一个通过忽略空值来创建唯一索引的解决方案。但是,我想扩展它,我无法达成解决方案。

我有一个表的3列的复合索引(表中还有其他10列)。这些列不是PK的一部分。在这3列中,2将始终保留一些值,第3列可能为NULL。我有大量的测试数据,并且有许多插入,其中2列具有相同的值,第3列为NULL。这些所有插件都适用于PostgreSQL,但Oracle抱怨道。为了让我的测试用例工作,我认为我认为最简单的解决方案是尝试一个独特的Oracle索引,它可以在PostgreSQL中运行。

准确地说:我想要一种以下类型的结构,不确定如何组合col1 + col2 + col3

create unique index tbl_idx on tbl (nvl2(col3, col1 + col2, col1 + col2 + col3))

我正在使用liquibase。索引按以下方式创建 -

<changeSet dbms="postgresql,oracle" author="abc" id="222">
    <createIndex indexName="Index_7" schemaName="ss" tableName="Users" unique="true">
        <column name="idOrganization"/>
        <column name="strUsername"/>
        <column name="strNotDeleted"/>
    </createIndex>
</changeSet>

我正在使用liquibase来创建我的测试数据,这里有两个插入语句

<insert schemaName="ss" tableName="Users">
    <column name="strUsername" value="user1" />
    <column name="idUser" valueNumeric="20741" />
    <column name="idOrganization" valueNumeric="4" />
    <column name="strFirstName" value="user" />
    <column name="strLastName" value="one" />
    <column name="strEmail" value="email@foo.com" />
    <column name="strNotDeleted" />
</insert>
<insert schemaName="ss" tableName="Users">
    <column name="strUsername" value="user1" />
    <column name="idUser" valueNumeric="20771" />
    <column name="idOrganization" valueNumeric="4" />
    <column name="strFirstName" value="user" />
    <column name="strLastName" value="one" />
    <column name="strEmail" value="email@foo.com" />
    <column name="strNotDeleted" />
</insert>

这两个插件适用于PostgreSQL,但是对于Oracle出现错误而导致错误&#34; Index_7约束违规&#34;。

2 个答案:

答案 0 :(得分:10)

如果目标只是为了防止将strNotDeleted设置为非NULL值的重复,那么你需要一个像这样的基于函数的索引

SQL> create table users(
  2    idOrganization number,
  3    strUsername    varchar2(100),
  4    strNotDeleted  varchar2(3)
  5  );

Table created.


SQL> create unique index idx_users
  2      on users( (case when strNotDeleted is not null
  3                      then idOrganization
  4                      else null
  5                  end),
  6                (case when strNotDeleted is not null
  7                      then strUsername
  8                      else null
  9                 end) );

Index created.

这允许插入您在问题中提到的两行

SQL> insert into users values( 4, 'user', null );

1 row created.

SQL> insert into users values( 4, 'user', null );

1 row created.

您可以在strNotNull列设置为非NULL值

的位置插入一行
SQL> insert into users values( 4, 'user', 'Yes' );

1 row created.

但是你无法插入第二行

SQL> insert into users values( 4, 'user', 'Yes' );
insert into users values( 4, 'user', 'Yes' )
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.IDX_USERS) violated

在幕后,Oracle b * -tree索引不会完全索引NULL条目。如果CASE不是idOrganization,则两个strUsername语句可确保索引仅包含strNotDeletedNULL的条目。如果strNotDeletedNULL,则两个CASE语句都会计算为NULL,并且索引中不会生成任何条目。从概念上讲,它类似于其他数据库中的部分索引,它允许您在索引上指定WHERE子句,以便只索引“有趣”的行。

答案 1 :(得分:0)

SQL> create table users(
      idOrganization number,
      strUsername    varchar2(100),
      strNotDeleted  varchar2(3)
   )
SQL> /

Table created.

SQL> Create unique index idx_users
    on users(
         (
           case when strNotDeleted is not null
                      then idOrganization
                        else null
             end
          ),
          (
           case when strNotDeleted is not null
                       then strUsername
                       else null
           end
         ),
         (
           case when strNotDeleted is not null
                      then strNotDeleted
                       else null
           end
         )
   )
SQL> /

Index created.