8

これは、Oracle データベース (10g) で実行した小さな実験です。(オラクルの)実装の利便性は別として、一部の挿入が受け入れられ、他の挿入が拒否される理由がわかりません。

create table sandbox(a number(10,0), b number(10,0));
create unique index sandbox_idx on sandbox(a,b);

insert into sandbox values (1,1); -- accepted
insert into sandbox values (1,2); -- accepted
insert into sandbox values (1,1); -- rejected

insert into sandbox values (1,null); -- accepted
insert into sandbox values (2,null); -- accepted
insert into sandbox values (1,null); -- rejected

insert into sandbox values (null,1); -- accepted
insert into sandbox values (null,2); -- accepted
insert into sandbox values (null,1); -- rejected

insert into sandbox values (null,null); -- accepted
insert into sandbox values (null,null); -- accepted

いくつかの列の値が不明な行が時々あることが理にかなっていると仮定すると、重複の防止に関連する 2 つの使用例が考え
られます。 1. 重複を拒否したいが、制約された列の値が不明な場合は受け入れたい
2. 制約された列の値が不明な場合でも、重複を拒否したい。

ただし、明らかに Oracle は別のものを実装して
ます。

ユースケース (2) に到達するために Oracle の実装を利用する方法を考えることができます。しかし、ユースケース(1)に到達する方法がわかりません。

つまり、どうすれば Oracle をこのように動作させることができるでしょうか?

create table sandbox(a number(10,0), b number(10,0));
create unique index sandbox_idx on sandbox(a,b);

insert into sandbox values (1,1); -- accepted
insert into sandbox values (1,2); -- accepted
insert into sandbox values (1,1); -- rejected

insert into sandbox values (1,null); -- accepted
insert into sandbox values (2,null); -- accepted
insert into sandbox values (1,null); -- accepted

insert into sandbox values (null,1); -- accepted
insert into sandbox values (null,2); -- accepted
insert into sandbox values (null,1); -- accepted

insert into sandbox values (null,null); -- accepted
insert into sandbox values (null,null); -- accepted
4

4 に答える 4

7

関数ベースのインデックスを試してください:

サンドボックスに一意のインデックス sandbox_idx を作成します(CASE WHEN a IS NULL THEN NULL WHEN b IS NULL THEN NULL ELSE a||','||b END);

この猫の皮を剥ぐ方法は他にもありますが、これはその 1 つです。

于 2009-03-23T22:28:37.683 に答える
7
create unique index sandbox_idx on sandbox
 (case when a is null or b is null then null else a end,
  case when a is null or b is null then null else b end);

機能的なインデックス!基本的に、無視する (つまり、受け入れる) すべてのタプルがすべて null に変換されるようにする必要がありました。醜いが、醜いわけではない。希望どおりに動作します。

別の質問への解決策の助けを借りてそれを考え出しました:データベーステーブルを制約して、列に特定の値を持つことができるのは1行だけですか?

そこに行って、トニー・アンドリュースにもポイントを与えてください。:)

于 2009-03-23T22:29:36.870 に答える
2

私はOracleの人ではありませんが、Oracleのインデックスに計算列を含めることができれば、うまくいくはずのアイデアがあります。

次のように計算されるテーブル (および UNIQUE インデックス) に追加の列を追加します。明らかな理由から、この追加の列を「ヌルバスター」と呼んでいます。

alter table sandbox add nullbuster as 
  case when a is null or b is null then pk else null end;
create unique index sandbox_idx on sandbox(a,b,pk);

この例は、2002 年頃に Usenet グループの microsoft.public.sqlserver.programming で何度も取り上げました。groups.google.com で「nullbuster」という単語を検索すると、ディスカッションを見つけることができます。Oracle を使用しているという事実は、あまり重要ではありません。

PS SQL Server では、このソリューションはフィルター処理されたインデックスにほとんど取って代わられています。

create unique index sandbox_idx on sandbox(a,b)
(where a is not null and b is not null);

あなたが参照したスレッドは、Oracleがこのオプションを提供していないことを示唆しています。別の選択肢であるインデックス付きビューの可能性もありませんか?

create view sandbox_for_unique as
select a, b from sandbox
where a is not null and b is not null;

create index sandbox_for_unique_idx on sandbox_for_unique(a,b);
于 2009-08-10T02:34:10.350 に答える
1

私はあなたができると思います。

ただし、記録のために、2 つの列に単純な一意のインデックスがある場合に Oracle がそのように動作する理由を説明するために段落を残します。

列が一意に索引付けされている場合、Oracle は 2 つの (1、null) ペアを受け入れません。

1 と null のペアは、「インデックス可能な」ペアと見なされます。2 つの null のペアにインデックスを付けることができないため、null と null のペアを好きなだけ挿入できます。

(1, null) は、1 にインデックスを付けることができるため、インデックスが付けられます。次に (1, null) を再度挿入しようとすると、インデックスによって 1 が取得され、一意の制約に違反します。

(null,null) は、インデックスを作成する値がないため、インデックスが作成されていません。そのため、一意の制約に違反していません。

于 2009-03-23T22:23:56.273 に答える