web-dev-qa-db-ja.com

nullにできないフィールドに対してPostgreSQLでNOT NULLを指定しないことの結果は何ですか?

私はアプリケーションを持っています(データはPostgreSQLに格納されています)。テーブルのフィールドの大部分は常にnullではありませんが、これらのテーブルのスキーマはこれを強制しません。たとえば、次の偽のテーブルを見てください。

CREATE TABLE "tbl" (
    "id" serial,
    "name" varchar(40),
    "num" int,
    "time" timestamp
    PRIMARY KEY ("id"),
    UNIQUE ("id")
);

また、namenumtimeNOT NULLとして明示的に記述されていません。実際には、アプリケーション側で適用されるためです。


私の考えでは、変更する必要があると思いますが、逆に、アプリケーションレベルではnull値がここに表示されないようにし、他の誰も手動でテーブルを変更しないようにします。

私の質問は:利点(パフォーマンス、ストレージ、一貫性など)と欠点(現時点でnullが存在しないことをすでに確認しており、ビジネスロジックから、明示的にNOT NULL制約を設定することでnullになりませんか?

私たちは適切なコードレビュープロセスと適度に優れたドキュメントを持っているので、新しい人がこの制約を破る何かをコミットする可能性は、変更を正当化するには実際には十分ではありません。

これは私の決定ではないので、これがまさに私が他の正当化を求めている理由です。私の意見では、何かがnullになり得ず、データベースで何かがnullでないことを指定できる場合、それを行うだけです。特に変更が非常に簡単な場合。

10
Salvador Dali

新しいプログラマーが到着し、そのdbに対してアプリを作成しなければならない場合はどうなりますか?彼らはそのフィールドx hasNOT NULLであることを知りません。

別のプログラムは、すべてのフィールドxがカウントを実行するためにNOT NULLであると想定する場合がありますが、新しいプログラムのために、一部は現在NULLであり、一貫性がなく、エラーの追跡が困難です。

IMHO常にデータに近い、つまりデータベース内のデータ整合性ルールを適用するのが最善です。そうすれば、新しいアプリやプログラマーがデータを台無しにすることはできません。

プログラマー、アプリケーション、言語、フレームワークが行き来します。データとデータベースは永続化する傾向があります。データベースは、一貫性のない、潜在的に誤ったデータに対する防御の最後の行です。

最大パフォーマンスを犠牲にしても、データベースの整合性制約強制メカニズムを使用します。正しい結果を生成する遅いシステムは無限に物事が間違っている高速なシステムよりも優れています!

9
Vérace

他のコメントですでに引用されているように、テーブル仕様にNOT NULLを追加すると、クエリのパフォーマンスが大幅に向上します。別の答えで述べられている非常に良い方法論的な理由に)。

その理由は、列がNULL値を持つことができないことを知っているクエリオプティマイザーは、NOT INNOT EXISTSの場合のように、そのような値に対する特別なテストを除外できるためです。たとえば、この blog を見ると、特定のクエリでフィールドNOT NULL(テーブルに常にnull以外の値が含まれている場合)を宣言しないと、実行時間が500%増加することがわかります。 。結果はSQL Serverで表示されますが、同様の動作が他のリレーショナルDBMSにも存在する可能性があります(データベースが他のシステムに移植される可能性があることは言うまでもありません)。想定できる一般的なルールは、クエリオプティマイザーがより多くの情報を利用できる場合、より効率的なアクセスプランを作成できるということです。

5
Renzo

スペースの影響

スペースの影響については、この投稿で@Erwin Brandstetterが語っています

簡単に言うと、データベースにtotalColumns - 8ビットを最も近いバイト(またはMAXALIGN)に切り上げて保存します。

  1. 8列以上
  2. [〜#〜]すべて[〜#〜]テーブルの列はNOT NULL

パフォーマンスへの影響

ただし、 @ Erwin BrandstetterによるSEに関するこの投稿 では、

  1. 「NOT NULLを設定しても、それ自体はパフォーマンスに影響を与えません。チェックのための数サイクル-無関係です。」
  2. 「...ダミー値の代わりにNULLを実際に使用する。データ型によっては、多くのディスク領域とRAMを節約できるため、すべてを高速化できます。」

@ Renzoに答えがあります パフォーマンスへの影響について話します-PostgreSQLに該当するものはないと仮定します。 PostgreSQLに関連するものとしてanyを実証するものは何も見つかりません。保存されたサイクルが何であれ、最も基本的なクエリでさえ数量化することはできません。

CREATE TABLE foo (
  a int,
  b int NOT NULL,
  x float,
  y float NOT NULL
);

INSERT INTO foo ( a, b, x, y )
SELECT x, x, x, x
FROM generate_series(1,1E7) AS X(x);

EXPLAIN ANALYZE SELECT 1/a FROM foo;
EXPLAIN ANALYZE SELECT 1/b FROM foo;
EXPLAIN ANALYZE SELECT 1/x FROM foo;
EXPLAIN ANALYZE SELECT 1/y FROM foo;

さらに、NULLインデックスがこれまでよりも高速であるかどうかを確認するためにいくつかのテストを実行しましたが、それを実証することができませんでした。これは、9.1のクエリプランナーが異なるWHERE句で部分インデックスを使用できることについて説明しているメーリングリストで、 Scott Marloweによる非常に便利なスレッド を見つけることができます。以下を実行してこれをテストしました

CREATE TABLE foo ( a int );
CREATE TABLE bar ( a int NOT NULL );
INSERT INTO foo
  SELECT null FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT null FROM generate_series(1,1e5) AS x
;
INSERT INTO bar
  SELECT 0 FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT 0 FROM generate_series(1,1e5) AS x
;

今、私はインデックスを作成しました、

CREATE INDEX foobar ON foo(a) WHERE a IS NOT NULL;
CREATE INDEX barbar ON bar(a) WHERE a <> 0;

これらのどちらの場合でも、プランナは= 10を選択するときにインデックスを使用でき、NULLまたは0をそれぞれ検索するときにseqスキャンを使用しました。両方の部分インデックスは同じサイズでした。また、完全なインデックス(表示されていません)は同じサイズでした。同じ方法に従って、1つのシーケンス1..1e5、1つのnull/0値、および別のシーケンス1..1e5をテーブルにロードしました。どちらの方法でも、テーブル全体をカバーするインデックスを持つnull/0を見つけることができました。

TLDR;概要

私は、プランナーの不備を含めるためにテストする価値があると私が考えたほとんどのパフォーマンスの懸念について、何らかの方法で何かを実証することはできません。 ramを節約するためにnullを使用する利点は本当です。 nullを使用しないことで節約されるディスク領域はごくわずかです。これは、1つのNULLABLE列、または8列未満のテーブルの誇張です。これらの場合、ディスク領域は節約されません。

2
Evan Carroll