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


10

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

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

またnamenumtime明示的として記載されていないNOT NULL彼らが実際に執行はアプリケーション側で起こるので、。


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

私の質問は次のとおりです。明示的なNOT NULL制約?

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

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


1
ヌルとディスクスペースの考慮事項については、この答えを参照してください。stackoverflow.com/questions/5008753/...要するに、あなたのテーブルが8つの以上の列と少なくとも1つのNULL可能列を持っている場合、テーブルはより多くのすべての列がある場合よりも、行あたりのバイト数が必要になりますnullでないと定義されています。
ypercubeᵀᴹ

1
@ypercubeᵀᴹ:正確には、行に実際のnull値がある場合にのみ、nullビットマップが行ごとに追加されます:stackoverflow.com/a/7654497/939860。したがって、NOT NULL制約がストレージサイズに直接影響することはありません。もちろん、すべての列が定義されているNOT NULLので、最初はnullビットマップはありません。一方、実際の値のない列に「空」またはダミーの値の代わりにNULLを使用すると、nullビットマップは比較的はるかに小さいため(まれなエッジの場合を除いて)、ストレージサイズは通常はるかに小さくなります。
Erwin Brandstetter 2016年

@ErwinBrandstetter私の悪いことは、その部分を理解していませんでした。したがって、null値を持たない列の場合、NULLとして定義してもNOT NULLとして定義しても、ストレージに実際の違いはありませんか?インデックスストレージスペースも同じですか?
ypercubeᵀᴹ

5
「アプリケーションレベルではここにnull値が表示されないようにしています」いいえ、表示されません。それは可能性があるアプリケーションがヌル挿入しないことを確認してください。しかし、私には(たとえば)psqlがあり、アプリケーションがそれを知らなくても、意図的に、または誤ってnullを挿入できます。
マイクシェリル「キャットリコール」、

5
誰も手動でテーブルを変更しないことを確認できる唯一のアプリケーションは、dbms自体です。
マイクシェリル「キャットリコール」

回答:


9

新しいプログラマーが到着し、そのデータベースに対してアプリを作成しなければならない場合はどうなりますか?彼らは、フィールドxがいることを知っていない持っていることNOT NULL

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

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

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

パフォーマンスを犠牲にしても、データベースの整合性制約の実施メカニズムを最大限に活用してください。正しい結果を生成する遅いシステムは、物事を誤る速いシステムよりもはるかに優れています!


1
IMHO it is always best to enforce data integrity rules as near to the data as possibleこれは実際に私が書いた腸の感覚と同じです。そして、これがまさに私が真の正当化を求めている理由です。私たちは適切なコードレビューと適切なドキュメントを持っているので、新しい開発者が何かを知らないという懸念は、変更を正当化するのに十分ではありません。
サルバドールダリ2016年

4
コードレビューと優れたドキュメントは、(プログラミングまたはその他の)エラーに対してあなたを保証するものではありません。
ypercubeᵀᴹ

2
そして、彼らが厳しい締め切りにあるプロジェクトに取り掛かる前に、ドキュメントのすべて(または一部でも)をREAL PROGRAMMERS読む人はどれくらいいますか?
Vérace

3
私はかつて、データウェアハウスに対して同じ態度をとっていた銀行でレビューを行いました。彼らの場合-参照整合性はありません。まあ、誰かがドキュメントを読んでおらず、ルックアップテーブルのデータを削除していないため、古いデータの40%がゴミでした。データの整合性を備えたコードレビューやドキュメントを信頼せず、データベースで明示的にする。
TomTom

5

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

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


ありがとうございました。これが私が探していた答えのタイプです。
サルバドールダリ2016年

5
NULLを決して含まない列はNOT NULL、複数の理由で定義する必要があります。それについての議論はありません。ただし、SQL Serverに関するブログへのリンクはPostgresには適用されず、言及されているパフォーマンスへの影響も証明されていません。何もないというわけではありませんが、実際の証拠を見たいです。
Erwin Brandstetter 2016年

@ErwinBrandstetter、私はPostgreSQLオプティマイザについて非常に高い期待を持っていました:(いくつかのテストを行った後、NOT NULL制約がある場合とない場合のPostgreSQLのブログに示されているNOT INクエリに大きな違いはありませんでした。 、そして私がそれを完全に削除するべきだと思うかどうか尋ねています
Renzo

いいえ、削除する必要はないと思います。1つには5票以上の投票があり、反対票はありません。
ypercubeᵀᴹ

not innull可能列のセマンティクスは異なりますが、2つの計画には多少の違いがあるはずですか?
マーティンスミス

2

スペースの影響

スペースへの影響は@Erwin Brandstetterことで、この記事での話されています

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

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

パフォーマンスへの影響

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

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

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

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句で部分インデックスを使用できることについて述べているメーリングリストで見つけることができます。以下を実行してこれをテストしました

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

TLDR; 概要

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.