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

dev*_*ang 1 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))
Run Code Online (Sandbox Code Playgroud)

我正在使用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>
Run Code Online (Sandbox Code Playgroud)

我使用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>
Run Code Online (Sandbox Code Playgroud)

这两个插件适用于PostgreSQL,但是Oracle失败并出现"Index_7约束违规"错误.

Jus*_*ave 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.
Run Code Online (Sandbox Code Playgroud)

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

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

1 row created.

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

1 row created.
Run Code Online (Sandbox Code Playgroud)

您可以在strNotNull列设置为非NULL值的位置插入一行

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

1 row created.
Run Code Online (Sandbox Code Playgroud)

但是你不能插入第二行

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
Run Code Online (Sandbox Code Playgroud)

在幕后,Oracle b*-tree索引不会完全索引NULL条目.这两个CASE语句确保索引只有条目idOrganization,strUsername如果strNotDeleted不是NULL.如果strNotDeletedNULL,则两个CASE语句都会求值,NULL并且索引中不会进行任何输入.从概念上讲,它类似于其他数据库中的部分索引,它允许您WHERE在索引上指定一个子句,以便只索引"有趣"的行.