PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Memahami Batasan Pemeriksaan di PostgreSQL

Mengelola data adalah tantangan besar. Saat dunia kita berputar, data terus tersebar luas, berlimpah, dan intensif. Oleh karena itu, kita harus mengambil tindakan untuk menangani arus masuk.

Memvalidasi setiap bagian data 'dengan tangan ' sepanjang waktu tidak praktis. Sungguh mimpi yang fantastis. Tapi, setelah semua, hanya itu. Mimpi. Data yang buruk adalah data yang buruk. Tidak peduli bagaimana Anda mengirisnya atau memotongnya (pun intended). Ini adalah masalah dari awal, yang mengarah ke lebih banyak masalah.

Basis data modern menangani sebagian besar pengangkatan berat untuk kita. Banyak yang menyediakan solusi bawaan untuk membantu mengelola area data khusus ini.

Cara pasti untuk mengontrol data yang dimasukkan ke dalam kolom tabel adalah dengan tipe data. Butuh kolom dengan angka desimal, memiliki jumlah digit total 4, dengan 2 di antaranya setelah desimal?

Tentu saja! Tidak ada masalah sama sekali.

NUMERIC(4,2), opsi yang layak, menjaga kolom itu seperti anjing penjaga. Bisakah nilai teks karakter masuk ke sana? Bukan kesempatan bola salju.

PostgreSQL menawarkan banyak tipe data. Kemungkinannya adalah, satu sudah ada untuk memenuhi kebutuhan Anda. Jika tidak, Anda dapat membuatnya sendiri. (Lihat:JENIS CREATE PostgreSQL)

Namun, tipe data saja tidak cukup. Anda tidak dapat memastikan bahwa persyaratan yang paling spesifik tercakup dan sesuai dengan penataan yang luas tersebut. Aturan kepatuhan dan semacam 'standar' biasanya diperlukan saat merancang skema.

Misalkan di kolom NUMERIC(4,2) yang sama, Anda hanya menginginkan nilai yang lebih besar dari 25,25 tetapi kurang dari 74,33? Jika nilai 88,22 disimpan, tipe data tidak salah. Dengan mengizinkan 4 digit total, dengan 2 paling banyak setelah desimal, ia melakukan tugasnya. Letakkan kesalahan di tempat lain.

Bagaimana kita menang di depan ini dalam hal mengontrol data yang diizinkan di database kita? Konsistensi data adalah prioritas utama dan merupakan bagian integral dari solusi data yang baik. Jika (tidak aktif) Anda mengontrol data yang dikumpulkan dari awal sumber asalnya, konsistensi kemungkinan tidak akan menjadi masalah.

Tapi, dunia yang sempurna hanya ada (mungkin) di salah satu dari banyak novel fantasi yang saya suka baca.

Sayangnya, data yang tidak lengkap, tidak konsisten, dan 'kotor' adalah karakteristik dan kenyataan yang terlalu umum hadir dalam bidang database-sentris.

Namun, tidak semua hilang dalam malapetaka dan kesuraman karena kami memiliki kendala Periksa untuk mengurangi masalah ini. Untuk aturan khusus tersebut, kita harus menerapkannya, karena kebutuhan, yang memastikan bahwa kita hanya menangani dan menyimpan data yang konsisten. Dengan mengamanatkan spesifikasi tersebut dalam database, kami dapat meminimalkan dampak data yang tidak konsisten terhadap tujuan dan solusi bisnis kami yang akan datang.

Apa itu Kendala? - Definisi Tingkat Tinggi

Dalam konteks ini, batasan adalah jenis aturan atau batasan yang ditempatkan pada kolom tabel database. Kekhususan ini mengharuskan data yang masuk harus memenuhi persyaratan yang ditetapkan sebelum disimpan. Persyaratan tersebut cenderung dibuat 'secara profesional' (dan sering kali) sebagai aturan bisnis . Ini bermuara pada tes boolean validasi untuk kebenaran. Jika data lolos (benar), itu disimpan. Jika tidak, tidak ada entri (salah).

Constraints Tersedia di PostgreSQL

Pada saat penulisan, dokumentasi PostgreSQL mencantumkan 6 kategori batasan.

Mereka adalah:

  • Periksa Batasan
  • Kendala Bukan-Null
  • Batasan Unik
  • Kunci Utama
  • Kunci Asing
  • Batasan Pengecualian

Periksa Batasan

Contoh sederhana untuk kolom INTEGER adalah melarang nilai lebih besar dari katakanlah, 100.

learning=> CREATE TABLE no_go(id INTEGER CHECK (id < 100));
CREATE TABLE
learning=> INSERT INTO no_go(id) VALUES(101);
ERROR: new row for relation "no_go" violates check constraint "no_go_id_check"
DETAIL: Failing row contains (101).

Seperti yang terlihat di atas, upaya untuk MEMASUKKAN nilai apa pun yang melanggar batasan Periksa akan gagal.

Periksa batasan tidak hanya memantau kolom selama INSERT, bahkan pernyataan UPDATE (dan lainnya misalnya, \copy dan COPY) harus mematuhi batasan juga.

Misalkan tabel no_go memiliki nilai ini:

learning=> TABLE no_go;
id 
----
55
(1 row)

PEMBARUAN pada nilai kolom id ke nilai yang tidak sesuai dengan batasan Periksa juga gagal:

learning=> UPDATE no_go SET id = 155
learning-> WHERE id = 55;
ERROR: new row for relation "no_go" violates check constraint "no_go_id_check"
DETAIL: Failing row contains (155).

Batasan pemeriksaan harus 'masuk akal' untuk tipe data kolom target. Tidak valid untuk mencoba dan membatasi kolom INTEGER untuk melarang penyimpanan nilai teks karena tipe data itu sendiri tidak mengizinkannya.

Lihat contoh ini di mana saya mencoba memaksakan jenis batasan Periksa itu selama pembuatan tabel:

learning=> CREATE TABLE num_try(id INTEGER CHECK(id IN ('Bubble', 'YoYo', 'Jack-In-The-Box')));
ERROR: invalid input syntax for integer: "Bubble"

Hidup Tanpa Batasan Pemeriksaan

Pepatah lama yang pernah saya dengar dan bergema dengan saya adalah:"Jangan lewatkan air sampai sumur mengering . "

Tanpa kendala Periksa, kita pasti bisa menghubungkan manfaat luar biasa mereka yang paling dihargai ketika Anda harus melakukannya tanpa mereka.

Ambil contoh ini…

Untuk memulai, kami memiliki tabel dan data ini yang mewakili material permukaan jejak:

learning=> SELECT * FROM surface_material;
surface_id | material 
------------+--------------
101 | Gravel
202 | Grass
303 | Dirt
404 | Turf
505 | Concrete
606 | Asphalt
707 | Clay
808 | Polyurethane
(8 rows)

Dan tabel ini dengan nama jejak dan id_permukaannya sendiri:

learning=> SELECT * FROM trails;
id | name | surface_id 
----+-----------------+------------
1 | Dusty Storm | 303
2 | Runners Trip | 808
3 | Pea Gravel Pass | 101
4 | Back 40 Loop | 404
(4 rows)

Kami ingin memastikan bahwa table trails hanya berisi surface_id untuk nilai yang sesuai pada table surface_material.

Ya ya saya tahu. Anda meneriaki saya.

"Tidak bisakah ini diurus dengan KUNCI ASING?!?"

Ya, itu bisa. Tapi saya menggunakannya untuk mendemonstrasikan penggunaan umum, bersama dengan jebakan yang perlu diketahui (disebutkan nanti dalam posting).

Tanpa batasan Check, Anda dapat menggunakan TRIGGER dan mencegah penyimpanan nilai yang tidak konsisten.

Berikut ini contoh kasar (tetapi berhasil):

CREATE OR REPLACE FUNCTION check_me()
RETURNS TRIGGER AS
$$
BEGIN
IF NEW.surface_id NOT IN (SELECT surface_id FROM surface_material)
THEN Raise Exception '% is not allowed for surface id', NEW.surface_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE PLpgSQL;
CREATE TRIGGER check_me BEFORE INSERT OR UPDATE ON trails
FOR EACH ROW EXECUTE PROCEDURE check_me();

Upaya INSERT nilai yang tidak memiliki surface_id yang sesuai di jalur tabel, gagal:

learning=> INSERT INTO trails(name, surface_id)
learning-> VALUES ('Tennis Walker', 110);
ERROR: 110 is not allowed for surface id
CONTEXT: PL/pgSQL function check_me() line 4 at RAISE

Hasil kueri di bawah mengonfirmasi 'melanggar ' nilai tidak disimpan:

learning=> SELECT * FROM trails;
id | name | surface_id 
----+-----------------+------------
1 | Dusty Storm | 303
2 | Runners Trip | 808
3 | Pea Gravel Pass | 101
4 | Back 40 Loop | 404
(4 rows)

Itu pasti banyak pekerjaan untuk melarang nilai yang tidak diinginkan.

Mari kita terapkan kembali persyaratan ini dengan batasan Periksa.

Karena Anda tidak dapat menggunakan subkueri (inilah sebabnya saya menggunakan contoh di atas) dalam definisi batasan Periksa yang sebenarnya, nilainya harus kode keras .

Untuk meja kecil, atau contoh sepele seperti yang disajikan di sini, ini bagus. Dalam skenario lain, dengan memasukkan lebih banyak nilai, Anda mungkin lebih baik mencari solusi alternatif.

learning=> ALTER TABLE trails ADD CONSTRAINT t_check CHECK (surface_id IN (101, 202, 303, 404, 505, 606, 707, 808));
ALTER TABLE

Di sini saya menamai batasan Periksa t_check versus membiarkan sistem menamainya.

(Catatan:Definisi sebelumnya check_me() FUNGSI dan yang menyertai TRIGGER dihapus (tidak ditampilkan) sebelum menjalankan di bawah MASUKKAN.)

learning=> INSERT INTO trails(name, surface_id)
VALUES('Tennis Walker', 110);
ERROR: new row for relation "trails" violates check constraint "t_check"
DETAIL: Failing row contains (7, Tennis Walker, 110).

Maukah Anda melihat betapa mudahnya itu! Tidak diperlukan PEMICU dan FUNGSI.

Periksa batasan membuat jenis pekerjaan ini mudah.

Ingin menjadi licik dalam definisi Periksa batasan?

Anda bisa.

Misalkan Anda membutuhkan tabel daftar jalur yang sedikit lebih ramah bagi mereka yang memiliki pergelangan kaki dan lutut yang sensitif. Tidak ada permukaan keras yang diinginkan di sini.

Anda ingin memastikan bahwa setiap jalur pendakian atau trek yang tercantum dalam tabel nice_trail memiliki material permukaan 'Kerikil' atau 'Kotoran'.

Batasan Pemeriksaan ini menangani persyaratan itu tanpa masalah:

learning=> CREATE TABLE nice_trail(id SERIAL PRIMARY KEY,
learning(> name TEXT, mat_surface_id INTEGER CONSTRAINT better_surface CHECK(id IN (101, 303))); 
CREATE TABLE

Itu benar-benar berfungsi dengan baik.

Tapi, bagaimana dengan FUNGSI yang mengembalikan kedua id yang diperlukan untuk membuat Cek berfungsi? Apakah FUNCTION diperbolehkan dalam definisi Periksa batasan?

Ya, satu bisa digabungkan.

Berikut adalah contoh kerja.

Pertama, badan fungsi dan definisi:

CREATE OR REPLACE FUNCTION easy_hike(id INTEGER)
RETURNS BOOLEAN AS
$$
BEGIN
IF id IN (SELECT surface_id FROM surface_material WHERE material IN ('Gravel', 'Dirt'))
THEN RETURN true;
ELSE RETURN false;
END IF;
END;
$$ LANGUAGE PLpgSQL;

Perhatikan dalam pernyataan CREATE TABLE ini, saya mendefinisikan batasan Periksa di 'tabel ' padahal sebelumnya saya hanya memberikan contoh di 'kolom ' tingkat.

Periksa batasan yang ditentukan pada tingkat tabel benar-benar valid:

learning=> CREATE TABLE nice_trail(nt_id SERIAL PRIMARY KEY,
learning(> name TEXT, mat_surface_id INTEGER,
learning(> CONSTRAINT better_surface_check CHECK(easy_hike(mat_surface_id)));
CREATE TABLE

Semua sisipan ini bagus:

learning=> INSERT INTO nice_trail(name, mat_surface_id)
learning-> VALUES ('Smooth Rock Loop', 101), ('High Water Bluff', 303);
INSERT 0 2

Sekarang hadir sebuah INSERT untuk jejak yang tidak memenuhi batasan pada kolom mat_surface_id:

learning=> INSERT INTO nice_trail(name, mat_surface_id)
learning-> VALUES('South Branch Fork', 404);
ERROR: new row for relation "nice_trail" violates check constraint "better_surface_check"
DETAIL: Failing row contains (3, South Branch Fork, 404).

Panggilan FUNCTION kami dalam definisi Batasan Periksa berfungsi seperti yang dirancang, membatasi nilai kolom yang tidak diinginkan.

Asap dan Cermin?

Apakah semuanya seperti yang terlihat dengan batasan Periksa? Semua hitam putih? Tidak ada fasad di depan?

Contoh yang patut diperhatikan.

Kami memiliki tabel sederhana di mana kami ingin nilai DEFAULT menjadi 10 untuk satu-satunya kolom INTEGER yang ada:

learning=> CREATE TABLE surprise(id INTEGER DEFAULT 10, CHECK (id <> 10));
CREATE TABLE

Tapi, saya juga menyertakan batasan Check yang melarang nilai 10, dengan mendefinisikan id tidak boleh sama dengan angka itu.

Yang mana yang akan menang hari ini? Batasan DEFAULT atau Periksa?

Anda mungkin terkejut mengetahui yang mana.

saya.

INSERT sewenang-wenang, berfungsi dengan baik:

learning=> INSERT INTO surprise(id) VALUES(17);
INSERT 0 1
learning=> SELECT * FROM surprise;
id 
----
17
(1 row)

Dan INSERT dengan nilai DEFAULT:

learning=> INSERT INTO surprise(id) VALUES(DEFAULT);
ERROR: new row for relation "surprise" violates check constraint "surpise_id_check"
DETAIL: Failing row contains (10).

Ups...

Sekali lagi, dengan sintaks alternatif:

learning=> INSERT INTO surprise DEFAULT VALUES;
ERROR: new row for relation "surprise" violates check constraint "surpise_id_check"
DETAIL: Failing row contains (10).

Batasan Periksa menang atas nilai DEFAULT.

Contoh Bola Ganjil

Batasan Periksa dapat muncul hampir di mana saja dalam definisi tabel selama pembuatan. Bahkan di tingkat kolom, itu dapat diatur pada kolom yang tidak terlibat dalam pemeriksaan apa pun.

Berikut adalah contoh untuk menggambarkan:

learning=> CREATE TABLE mystery(id_1 INTEGER CHECK(id_2 > id_3),
learning(> id_2 INTEGER, id_3 INTEGER);
CREATE TABLE

INSERT untuk menguji batasan:

learning=> INSERT INTO mystery(id_1, id_2, id_3) VALUES (1, 2, 3);
ERROR: new row for relation "mystery" violates check constraint "mystery_check"
DETAIL: Failing row contains (1, 2, 3).

Berfungsi sebagaimana mestinya.

VALIDASI dan TIDAK VALID

Kami memiliki tabel dan data sederhana ini:

learning=> CREATE TABLE v_check(id INTEGER);
CREATE TABLE
learning=> INSERT INTO v_check SELECT * FROM generate_series(1, 425);
INSERT 0 425

Misalkan kita sekarang perlu menerapkan batasan Periksa yang melarang nilai apa pun yang kurang dari 50.

Bayangkan ini adalah meja besar dalam produksi dan kami tidak dapat benar-benar membeli kunci yang diperoleh saat ini, yang dihasilkan dari pernyataan ALTER TABLE. Namun, kendala ini perlu diatasi, terus maju.

ALTER TABLE akan memperoleh kunci (tergantung pada setiap subformulir yang berbeda). Seperti yang disebutkan, tabel ini sedang dalam produksi, jadi kami ingin menunggu sampai kami keluar dari 'jam sibuk '.

Anda dapat menggunakan opsi NO VALID saat membuat batasan Periksa:

learning=> ALTER TABLE v_check ADD CONSTRAINT fifty_chk CHECK(id > 50) NOT VALID; 
ALTER TABLE
Unduh Whitepaper Hari Ini Pengelolaan &Otomatisasi PostgreSQL dengan ClusterControlPelajari tentang apa yang perlu Anda ketahui untuk menerapkan, memantau, mengelola, dan menskalakan PostgreSQLUnduh Whitepaper

Melanjutkan operasi, jika ada upaya INSERT atau UPDATE yang melanggar batasan Periksa:

learning=> INSERT INTO v_check(id) VALUES(22);
ERROR: new row for relation "v_check" violates check constraint "fifty_chk"
DETAIL: Failing row contains (22).

Nilai kolom 'menyinggung' dilarang.

Kemudian, selama waktu henti, kami memvalidasi batasan Periksa untuk menerapkannya terhadap (setiap) kolom yang sudah ada sebelumnya yang mungkin melanggar:

learning=> ALTER TABLE v_check VALIDATE CONSTRAINT fifty_chk;
ERROR: check constraint "fifty_chk" is violated by some row

Pesannya cukup samar menurut saya. Namun, ini memberi tahu kami bahwa ada baris yang tidak sesuai dengan batasan.

Berikut adalah beberapa poin penting yang ingin saya sertakan dari dokumentasi ALTER TABLE (Verbiage langsung dari dokumen dalam tanda kutip):

  • Sintaks:ADD table_constraint [ NOT VALID ] - Deskripsi yang menyertai (sebagian) "Formulir ini menambahkan batasan baru ke tabel menggunakan sintaks yang sama dengan CREATE TABLE, ditambah opsi NOT VALID, yang saat ini hanya diperbolehkan untuk kunci asing dan PERIKSA batasan. Jika batasan ditandai NOT VALID, pemeriksaan awal yang berpotensi panjang untuk memverifikasi bahwa semua baris dalam tabel memenuhi batasan akan dilewati."
  • Sintaks:VALIDATE CONSTRAINT constraint_name - Deskripsi yang menyertai (sebagian) "Formulir ini memvalidasi kunci asing atau memeriksa batasan yang sebelumnya dibuat sebagai NOT VALID, dengan memindai tabel untuk memastikan tidak ada baris yang batasannya tidak terpenuhi. " "Validasi hanya memperoleh kunci EKSKLUSIF SHARE UPDATE pada tabel yang sedang diubah."

Sebagai tambahan, dua poin yang perlu diperhatikan saya pelajari di sepanjang jalan. Fungsi pengembalian-set dan subkueri tidak diizinkan di Periksa definisi batasan. Saya yakin ada yang lain dan saya menyambut umpan balik tentang mereka di komentar di bawah.

Periksa kendala luar biasa. Menggunakan solusi 'bawaan' yang disediakan oleh database PostgreSQL itu sendiri, untuk menerapkan pembatasan data apa pun sangat masuk akal. Waktu dan upaya yang dihabiskan untuk menerapkan Periksa batasan untuk kolom yang diperlukan, jauh lebih banyak daripada tidak menerapkannya sama sekali. Sehingga menghemat waktu dalam jangka panjang. Semakin kita bersandar pada database untuk menangani persyaratan semacam ini, semakin baik. Memungkinkan kami untuk fokus dan menerapkan sumber daya kami ke area/aspek lain dari manajemen basis data.

Terima kasih telah membaca.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengonversi dari Replikasi Asinkron ke Sinkron di PostgreSQL

  2. Dialek perlu diberikan secara eksplisit pada v4.0.0

  3. 5 Alat Pemantauan Kueri PostgreSQL Teratas

  4. Cara Mengonfigurasi Replikasi Cluster-to-Cluster untuk PostgreSQL

  5. Apa perbedaan antara LATERAL JOIN dan subquery di PostgreSQL?