web-dev-qa-db-ja.com

一意の列にnullを許可する

次の表を作成しました。

CREATE TABLE MMCompany (
   CompanyUniqueID BIGSERIAL PRIMARY KEY NOT NULL, 
   Name VARCHAR (150) NOT NULL,
   PhoneNumber VARCHAR(20) NOT NULL UNIQUE, 
   Email VARCHAR(75) UNIQUE,
   CompanyLogo BYTEA
 );

電子メールの列は一意であり、nullのレコードは1つしか存在しないため、私のシナリオでは「バグ」が発生します。私は同じ電子メールのない企業の記録を達成しようとしていますが、同時に企業に電子メールがないようにしています。

どうすればそれを達成できますか?

32
liv a

これは誤解です。
UNIQUE制約は、必要なものをexactly実行します。複数のNULL値は、UNIQUEで定義された列に共存できます。

引用 NIQUE制約に関するマニュアル

一般に、制約に含まれるすべての列の値が等しい表に複数の行がある場合、一意制約に違反します。ただし、この比較では2つのNULL値は等しいと見なされません。つまり、一意の制約が存在する場合でも、制約された列の少なくとも1つにnull値を含む重複行を格納できます。この動作はSQL標準に準拠していますが、他のSQLデータベースはこの規則に従わない可能性があると聞いています。そのため、移植性のあるアプリケーションを開発する場合は注意してください。

大胆な強調鉱山。

文字型 は空の文字列('')を許可します。これはNULL値ではなくnotであり、一意の違反をトリガーします複数の行に入力された場合の、他の非ヌル値と同様です。

71

Postgresにはそのような問題はありません

Erwin Brandstetterの正しい answer で、彼はあなたが本当に望む振る舞いを見るべきであると説明しています(ユニーク制約では複数のNULLが許可されています)。この動作は、特にSQL標準に準拠した一般的なデータベースと同様に、特にPostgresで確認できます。

他のデータベースの回避策

ただし、Postgresのドキュメントでは、一部のデータベースがこの機能に違反していることがわかっているため、移植性について警告しています。そのような非準拠システムの場合、そのようなフィールドでのNULL値の使用を偽の値に置き換えることをお勧めします。偽の値は、「unknown_」などの文字列と、実質的に一意であることが確実な任意の値です。その任意の値は、現在の日時に乱数を加えたものになります。

UUID

ただし、独自の任意の値をロールするのではなく、 [〜#〜] uuid [〜#〜] を生成します。元のバージョン1 UUIDは、実際には現在の日時、乱数、およびコンピューターの実質的に一意の MACアドレス の組み合わせです。

ハイフンを使用した標準形式の16進文字列として表示されるUUIDは、次のようになります。

93e6f268-5c2d-4c63-9d9c-40e6ac034f88

したがって、私の提案は、「unknown_」などの任意の文字列とUUIDを組み合わせて、次のようにすることです。

unknown_93e6f268-5c2d-4c63-9d9c-40e6ac034f88

したがって、非準拠データベースに対する私の提案は、そのような値を生成してNULLの代わりに使用し、特定の行の列に既知の値がまだない場合に使用することです。その列にNULL値を持つ(または持たない)行を検索するクエリを作成する代わりに、任意の文字列で始まる値を持つ(または持たない)行を検索するクエリを作成します。例。各行は、一意の値を持つという制約を満たします。

実際、この「unknown _」+ UUID値をその列のデフォルトとして割り当てます。

この列にNOT NULL制約を追加することもできます。

UUID値の生成

PostgresにはUUIDのデータ型の組み込みサポートがありますが、ここでのこの回答には関係ありません。必要なのは、UUID valueを生成することです。

UUIDを生成するには、この機能をPostgresに追加する拡張機能(プラグイン)が必要です。ほとんどのPostgresインストーラーには、このような拡張機能が含まれています。この拡張機能は id-ossp と呼ばれます。通常、拡張機能はデフォルトでは有効化されていません。 Postgresの最新バージョンでこれを行うには、 CREATE EXTENSION コマンドを使用します。手順については、 Postgres 9.1以降にインストールする または Postgres 9.0以前にある他の投稿 に関する私のブログ投稿を参照してください。拡張機能/プラグインがコンパイルされ、Postgresインストールにバンドルされていれば、新旧両方のインストール方法は簡単です。

概要

PostgresがSQL標準に準拠しているため、Postgresだけではこの回避策は必要ありません。しかし:

  • 他の非準拠データベースシステムへのコードの移植性について懸念がある場合、または
  • 非準拠のデータベースシステムとデータを交換する必要がある、または
  • あなたは Dr。Chris Date NULLは悪魔の働きであり、避けるべきであることに同意します

…次に、このような回避策が必要です。

17
Basil Bourque

SQL Serverのドキュメント には、「複数のnull値は重複と見なされる」と記載されているなど、一部のデータベースでは複数のnull値が許可されていません。 null許容のUNIQUE制約を許可しないデータベースでは、これを試すことができます( GuidoGの答え から別の質問へ):

CREATE UNIQUE NONCLUSTERED INDEX IDX_Email
ON MMCompany (Email)
WHERE Email IS NOT NULL;
6
dumbledad

テーブルからメール列を削除します。 NOT NULLおよびUNIQUEにできる新しいテーブルに配置します。

CREATE TABLE CompanyEmail
 (
    CompanyUniqueID INT NOT NULL PRIMARY KEY
       REFERENCES MMCompany (CompanyUniqueID),
    Email VARCHAR(75) NOT NULL UNIQUE
 );

Null許容のUNIQUE制約は避けてください。

4
nvogel

Nullは定義では定義されていないため、一意のnullはあまりうまくいきません。2つのnullが同じ不明かどうかはわかりません。

この意味で、電子メールに対する現在の独自の制約は正しいことであり、そのまま機能するはずです。


ただし、別の方法で作成する必要がある場合は、部分インデックスが機能します。

create unique index on MMCompany((email is null)) where (email is null);

別のアプローチは、制約トリガーを定義することです。何かのようなもの:

create function email_chk() returns trigger as $$
begin
  if exists (
    select 1 from mmcompany where email is null and companyuniqueid <> new.id
  ) then
    raise 'dup null found';
  end if;
  return null;
end;
$$ language plpgsql;

create constraint trigger after insert or update on mmcompany
for each row when (new.email is null)
execute procedure email_chk();
2