私はアプリケーションを持っています(データはPostgreSQLに格納されています)。テーブルのフィールドの大部分は常にnullではありませんが、これらのテーブルのスキーマはこれを強制しません。たとえば、次の偽のテーブルを見てください。
CREATE TABLE "tbl" (
"id" serial,
"name" varchar(40),
"num" int,
"time" timestamp
PRIMARY KEY ("id"),
UNIQUE ("id")
);
また、name
、num
、time
はNOT NULL
として明示的に記述されていません。実際には、アプリケーション側で適用されるためです。
私の考えでは、変更する必要があると思いますが、逆に、アプリケーションレベルではnull値がここに表示されないようにし、他の誰も手動でテーブルを変更しないようにします。
私の質問は:利点(パフォーマンス、ストレージ、一貫性など)と欠点(現時点でnullが存在しないことをすでに確認しており、ビジネスロジックから、明示的にNOT NULL
制約を設定することでnullになりませんか?
私たちは適切なコードレビュープロセスと適度に優れたドキュメントを持っているので、新しい人がこの制約を破る何かをコミットする可能性は、変更を正当化するには実際には十分ではありません。
これは私の決定ではないので、これがまさに私が他の正当化を求めている理由です。私の意見では、何かがnullになり得ず、データベースで何かがnullでないことを指定できる場合、それを行うだけです。特に変更が非常に簡単な場合。
新しいプログラマーが到着し、そのdbに対してアプリを作成しなければならない場合はどうなりますか?彼らはそのフィールドx hasがNOT NULL
であることを知りません。
別のプログラムは、すべてのフィールドxがカウントを実行するためにNOT NULL
であると想定する場合がありますが、新しいプログラムのために、一部は現在NULL
であり、一貫性がなく、エラーの追跡が困難です。
IMHO常にデータに近い、つまりデータベース内のデータ整合性ルールを適用するのが最善です。そうすれば、新しいアプリやプログラマーがデータを台無しにすることはできません。
プログラマー、アプリケーション、言語、フレームワークが行き来します。データとデータベースは永続化する傾向があります。データベースは、一貫性のない、潜在的に誤ったデータに対する防御の最後の行です。
最大パフォーマンスを犠牲にしても、データベースの整合性制約強制メカニズムを使用します。正しい結果を生成する遅いシステムは無限に物事が間違っている高速なシステムよりも優れています!
他のコメントですでに引用されているように、テーブル仕様にNOT NULL
を追加すると、クエリのパフォーマンスが大幅に向上します。別の答えで述べられている非常に良い方法論的な理由に)。
その理由は、列がNULL
値を持つことができないことを知っているクエリオプティマイザーは、NOT IN
とNOT EXISTS
の場合のように、そのような値に対する特別なテストを除外できるためです。たとえば、この blog を見ると、特定のクエリでフィールドNOT NULL
(テーブルに常にnull以外の値が含まれている場合)を宣言しないと、実行時間が500%増加することがわかります。 。結果はSQL Serverで表示されますが、同様の動作が他のリレーショナルDBMSにも存在する可能性があります(データベースが他のシステムに移植される可能性があることは言うまでもありません)。想定できる一般的なルールは、クエリオプティマイザーがより多くの情報を利用できる場合、より効率的なアクセスプランを作成できるということです。
スペースの影響については、この投稿で@Erwin Brandstetterが語っています
簡単に言うと、データベースにtotalColumns - 8
ビットを最も近いバイト(またはMAXALIGN
)に切り上げて保存します。
NOT NULL
ただし、 @ Erwin BrandstetterによるSEに関するこの投稿 では、
@ 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を見つけることができました。
私は、プランナーの不備を含めるためにテストする価値があると私が考えたほとんどのパフォーマンスの懸念について、何らかの方法で何かを実証することはできません。 ramを節約するためにnullを使用する利点は本当です。 nullを使用しないことで節約されるディスク領域はごくわずかです。これは、1つのNULLABLE
列、または8列未満のテーブルの誇張です。これらの場合、ディスク領域は節約されません。