Apa konsekuensi dari tidak menentukan TIDAK NULL di PostgreSQL untuk bidang yang tidak boleh nol?

10

Saya memiliki aplikasi (data disimpan dalam PostgreSQL), di mana sebagian besar bidang dalam tabel selalu tidak nol, tetapi skema untuk tabel ini tidak memberlakukan ini. Sebagai contoh, lihat tabel palsu ini:

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

Juga name, num, timetidak secara eksplisit dinyatakan sebagai NOT NULL, dalam kenyataannya mereka, karena penegakan terjadi di sisi aplikasi.


Perasaan saya adalah bahwa itu harus diubah, tetapi sebaliknya adalah bahwa tingkat aplikasi memastikan bahwa nilai nol tidak dapat muncul di sini dan tidak ada orang lain yang secara manual memodifikasi tabel.

Pertanyaan saya adalah : Apa manfaatnya (kinerja, penyimpanan, konsistensi, sesuatu yang lain) dan kelemahannya (dengan asumsi saya sudah memverifikasi bahwa tidak ada null yang ada saat ini, dan dari logika bisnis seharusnya tidak ada null) dengan menetapkan NOT NULLkendala eksplisit ?

Kami memiliki proses peninjauan kode yang baik dan dokumentasi yang cukup baik, sehingga kemungkinan bahwa beberapa orang baru akan melakukan sesuatu yang melanggar batasan ini tidak benar-benar cukup untuk membenarkan perubahan.

Ini bukan keputusan saya, jadi inilah tepatnya mengapa saya mencari pembenaran lain. Menurut pendapat saya, jika sesuatu tidak bisa menjadi nol dan database memungkinkan Anda menentukan bahwa sesuatu itu bukan nol - maka lakukan saja. Apalagi jika ubahannya super sederhana.

Salvador Dali
sumber
1
Lihat jawaban ini untuk pertimbangan Nulls dan ruang disk: stackoverflow.com/questions/5008753/... Singkatnya, jika tabel Anda memiliki lebih dari 8 kolom dan setidaknya 1 kolom nullable, tabel akan membutuhkan lebih banyak byte per baris daripada jika semua kolom adalah didefinisikan bukan nol.
ypercubeᵀᴹ
1
@ ypercubeᵀᴹ: Lebih tepatnya, bitmap null hanya ditambahkan per baris jika ada nilai null aktual di baris: stackoverflow.com/a/7654497/939860 . Oleh karena itu, NOT NULLkendala tidak memiliki efek langsung pada ukuran penyimpanan. Tentu saja, dengan semua kolom didefinisikan NOT NULL, tidak mungkin ada bitmap nol untuk memulai. Di sisi lain: ukuran penyimpanan biasanya jauh lebih kecil jika Anda menggunakan NULL alih-alih nilai "kosong" atau dummy untuk kolom tanpa nilai aktual, karena bitmap null relatif jauh lebih kecil (kecuali untuk kasus tepi langka).
Erwin Brandstetter
@ ErwinBrandstetter saya buruk itu, tidak mengerti bagian itu. Jadi untuk kolom yang tidak memiliki nilai null, tidak ada perbedaan nyata dalam penyimpanan, apakah Anda mendefinisikannya sebagai NULL atau NOT NULL, benar? Apakah itu sama untuk ruang penyimpanan indeks juga?
ypercubeᵀᴹ
5
"tingkat aplikasi memastikan bahwa nilai nol tidak dapat muncul di sini" Tidak, tidak. Ini mungkin memastikan bahwa satu aplikasi tidak insert nulls. Tapi saya punya psql (misalnya), dan saya bisa memasukkan null secara sengaja dan tidak sengaja tanpa aplikasi Anda mengetahuinya.
Mike Sherrill 'Cat Recall'
5
Satu-satunya aplikasi yang dapat memastikan tidak ada yang memodifikasi tabel secara manual adalah dbms itu sendiri.
Mike Sherrill 'Cat Recall'

Jawaban:

9

Apa yang terjadi ketika seorang programmer baru tiba dan harus menulis aplikasi terhadap db itu? Mereka tidak tahu bidang x itu seharusnya NOT NULL.

Program lain mungkin berasumsi bahwa semua bidang x adalah NOT NULLuntuk melakukan penghitungan katakan, tetapi beberapa sekarang adalah NULLkarena program baru, yang mengarah ke kesalahan yang tidak konsisten dan sulit dilacak.

IMHO itu selalu yang terbaik untuk menegakkan aturan integritas data sedekat mungkin dengan data, yaitu dalam database. Dengan begitu, aplikasi dan / atau programmer baru tidak dapat mengacaukan data Anda.

Pemrogram, aplikasi, bahasa dan kerangka kerja datang dan pergi. Data dan basis data cenderung bertahan. Basis data adalah garis pertahanan terakhir Anda terhadap data yang tidak konsisten dan berpotensi salah.

Manfaatkan secara maksimal mekanisme penegakan kendala integritas database Anda, bahkan dengan mengorbankan kinerja. Sistem lambat yang menghasilkan hasil yang benar jauh lebih unggul daripada yang cepat yang salah!

Vérace
sumber
1
IMHO it is always best to enforce data integrity rules as near to the data as possibleini sebenarnya sama dengan firasat yang saya tulis. Dan inilah tepatnya mengapa saya mencari pembenaran yang nyata. Kami memiliki ulasan kode di tempat dan dokumentasi yang baik, sehingga kekhawatiran tentang pengembang baru yang tidak mengetahui sesuatu tidak cukup untuk membenarkan perubahan.
Salvador Dali
4
Ulasan kode dan dokumentasi yang baik tidak menjamin Anda terhadap kesalahan (pemrograman atau lainnya).
ypercubeᵀᴹ
2
Dan berapa banyak yang REAL PROGRAMMERSmembaca semua (atau bahkan ada) dari dokumentasi sebelum terjebak dalam sebuah prject di mana mereka berada pada tenggat waktu yang ketat?
Vérace
3
Saya pernah melakukan review di bank yang memiliki sikap yang sama untuk data warehouse mereka. Dalam kasus mereka - tidak ada integritas referensial. Nah, yang terjadi 40% dari data lama adalah sampah karena seseorang belum membaca dokumentasi dan menghapus data dalam tabel pencarian. Anda tidak percaya ulasan kode dan dokumentasi dengan integritas data - Anda membuatnya eksplisit dalam database.
TomTom
5

Seperti yang sudah dikutip oleh orang lain dalam komentar, menambahkan NOT NULLspesifikasi tabel Anda dapat meningkatkan kinerja kueri Anda secara signifikan (selain alasan metodologi yang sangat baik yang dinyatakan dalam jawaban lain).

Alasannya adalah bahwa optimizer kueri, mengetahui bahwa kolom tidak dapat memiliki NULLnilai, dapat mengecualikan pengujian khusus untuk nilai-nilai tersebut, seperti dalam kasus NOT INvs. NOT EXISTSMisalnya , Anda dapat melihat blog ini , yang menunjukkan bahwa tidak mendeklarasikan bidang NOT NULL(saat tabel berisi selalu nilai-nilai bukan nol) dengan kueri tertentu meningkatkan waktu pelaksanaan 500%. Hasilnya ditampilkan untuk SQL Server, tetapi perilaku serupa dapat hadir dalam DBMS relasional lainnya, seperti milik Anda (belum lagi fakta bahwa database Anda dapat diangkut ke sistem lain). Aturan umum yang dapat Anda asumsikan adalah bahwa ketika lebih banyak informasi tersedia untuk optimizer kueri, maka rencana akses yang lebih efisien dapat dihasilkan.

Renzo
sumber
Terima kasih. Ini adalah jenis jawaban yang saya cari.
Salvador Dali
5
Kolom yang tidak pernah berisi NULL, harus didefinisikan NOT NULLkarena berbagai alasan, tidak ada argumen tentang itu. Tetapi tautan ke blog tentang SQL Server tidak berlaku untuk Postgres dan tidak membuktikan implikasi kinerja yang Anda sebutkan. Tidak mengatakan tidak ada, tetapi saya ingin melihat bukti nyata .
Erwin Brandstetter
@ErwinBrandstetter, saya memiliki banyak harapan tentang pengoptimal PostgreSQL :( Setelah beberapa tes saya tidak menemukan perbedaan signifikan dalam permintaan NOT IN yang disajikan dalam blog di PostgreSQL dengan dan tanpa batasan NOT NULL. Jadi, saya mengubah jawabannya , dan saya bertanya kepada Anda apakah Anda berpikir bahwa saya harus menghapusnya sama sekali
Renzo
Tidak, saya tidak berpikir itu harus dihapus. Ini memiliki 5+ suara dan tidak ada downvote, untuk satu.
ypercubeᵀᴹ
Semantik not inuntuk kolom nullable berbeda, jadi harus ada beberapa perbedaan dalam rencana antara keduanya?
Martin Smith
2

Implikasi ruang

The implikasi ruang dibicarakan di posting ini dengan @Erwin Brandstetter

Singkatnya, Anda akan menyimpan satu totalColumns - 8bit yang dibulatkan ke byte terdekat (atau MAXALIGN), jika database Anda memiliki

  1. Lebih dari 8 kolom
  2. SEMUA kolom di atas adalahNOT NULL

Implikasi kinerja

Namun, dalam posting ini di SE oleh @Erwin Brandstetter , katanya

  1. "Pengaturan NOT NULL tidak memiliki efek pada kinerja. Beberapa siklus untuk pemeriksaan - tidak relevan."
  2. "... dengan menggunakan NULLs alih-alih nilai dummy. Tergantung pada tipe data, kamu dapat menghemat banyak ruang disk dan RAM, dengan demikian mempercepat .. semuanya."

@Renzo memiliki jawaban yang berbicara tentang implikasi kinerja - saya akan menganggap tidak ada yang berlaku untuk PostgreSQL . Saya tidak dapat menemukan apa pun yang mendukung semua itu sebagai relevan dengan PostgreSQL. Siklus apa pun yang disimpan tidak dapat dikuantifikasi bahkan dalam kueri yang paling mendasar sekalipun.

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;

Selain itu saya menjalankan beberapa tes untuk melihat apakah NULL-indeks lebih cepat, dan saya tidak bisa membuktikannya. Anda dapat menemukan utas yang sangat berguna ini oleh Scott Marlowe di milis yang berbicara tentang perencana kueri di 9.1 yang dapat menggunakan indeks parsial pada klausa WHERE yang berbeda. Saya menguji ini dengan menjalankan yang berikut ini

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
;

Sekarang saya membuat indeks,

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

Dalam kedua kasus ini perencana dapat menggunakan indeks saat memilih = 10dan menggunakan pemindaian seq saat mencari masing-masing NULL atau 0. Kedua indeks parsial memiliki ukuran yang sama. Dan, indeks lengkap (tidak ditampilkan) memiliki ukuran yang sama. Mengikuti metodologi yang sama saya memuat tabel dengan satu urutan 1..1e5, dan satu nilai nol / 0, dan urutan lain dari 1..1e5. Kedua metode dapat menemukan nol / 0 dengan indeks yang mencakup seluruh tabel.

TLDR; Ringkasan

Saya tidak dapat membuktikan sesuatu dengan cara apa pun pada sebagian besar masalah kinerja yang saya pikir layak untuk diuji termasuk ketidakcukupan perencana. Manfaat menggunakan null untuk menghemat ram adalah nyata. Ruang disk yang disimpan dengan tidak menggunakan null dapat diabaikan, dan itu terlalu berlebihan pada tabel dengan satu NULLABLEkolom, atau kurang dari 8 kolom. Dalam kasus tersebut tidak ada ruang disk yang disimpan.

Evan Carroll
sumber