3

nullを無視して一意のインデックスを作成するソリューションを提供するSOのこの例に出くわしました。ただし、それを拡張したいのですが、解決策に到達できません。

テーブルの 3 つの列に複合インデックスがあります (テーブルには他に 10 列あります)。これらの列は PK の一部ではありません。これらの 3 つの列のうち、2 つは常に何らかの値を保持し、3 番目は NULL の場合があります。膨大なテスト データがあり、2 つの列が同じ値で 3 番目の列が NULL の挿入が多数あります。これらのすべての挿入は PostgreSQL ではうまく機能しますが、Oracle は文句を言います。私のテストケースが機能するための最も簡単な解決策は、PostgreSQL で機能するのと同じように機能する Oracle の一意のインデックスを試すことだと思います。

正確には: 次の種類の構成が必要ですが、結合方法がわかりませんcol1 + col2 + col3

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

リキベースを使用しています。インデックスは次の方法で作成されます -

<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を使用しています。ここに2つの挿入ステートメントがあります

<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>

これら 2 つの挿入は PostgreSQL では正常に機能しますが、Oracle では「Index_7 制約違反」というエラーで失敗します。

4

2 に答える 2

14

が NULL 以外の値に設定されている場所での重複を防ぐことのみが目的の場合は、strNotDeletedこのような関数ベースのインデックスが必要です。

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.

これにより、質問で言及した2行を挿入できます

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

1 row created.

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

1 row created.

strNotNull列が NULL 以外の値に設定されている1 つの行を挿入できます

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

1 row created.

ただし、そのような行を2番目に挿入することはできません

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エントリを完全にインデックス化するわけではありません。この 2 つのCASEステートメントにより、インデックスにidOrganizationおよびstrUsernameif strNotDeletedis notのエントリのみが含まれるようになりますNULLstrNotDeletedがの場合NULL、両方のCASEステートメントが に評価さNULLれ、インデックスにエントリは作成されません。概念的には、他のデータベースの部分インデックスに似ており、インデックスにWHERE句を指定して、「興味深い」行のみにインデックスを付けることができます。

于 2012-08-10T01:05:26.543 に答える
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.
于 2014-12-16T10:26:03.467 に答える