I came across this example on SO which gives a solution to create a unique index by ignoring nulls. However, I want to extend it and I am not able reach to a solution.
I have a composite index for 3 columns of a table (there are other 10 columns in the table). These columns are not part of PK. Of these 3 columns, 2 will always hold some value and 3rd may be NULL. I have huge test data, and there are many inserts with 2 of the columns with same value and 3rd column NULL. These all inserts work well for PostgreSQL, however Oracle complains. For my test cases to work, I think the simplest solution that I think is to try a unique index for Oracle that would work as it works in PostgreSQL.
Precisely: I want a construct of following kind, not sure how to combine col1 + col2 + col3
create unique index tbl_idx on tbl (nvl2(col3, col1 + col2, col1 + col2 + col3))
I am using liquibase. Index is created in following way –
<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>
I am using liquibase to create my test data, here are two insert statements
<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>
These 2 inserts work fine for PostgreSQL, however fail for Oracle with error “Index_7 constraint violation”.
If the goal is only to prevent duplicates where
strNotDeletedis set to a non-NULL value, then you want a function-based index like thisThis allows the two rows you mention in your question to be inserted
You can insert one row where the
strNotNullcolumn is set to a non-NULL valueBut you then can’t insert a second such row
Behind the scenes, an Oracle b*-tree index does not index completely
NULLentries. The twoCASEstatements ensure that the index only has entries foridOrganizationandstrUsernameifstrNotDeletedis notNULL. IfstrNotDeletedisNULL, then bothCASEstatements evaluate toNULLand no entry is made in the index. Conceptually, it’s similar to a partial index in other databases which allows you to specify aWHEREclause on your index so that you only index “interesting” rows.