PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Hiểu các ràng buộc kiểm tra trong PostgreSQL

Quản lý dữ liệu là một thách thức lớn. Khi thế giới của chúng ta thay đổi, dữ liệu tiếp tục phổ biến, phong phú và chuyên sâu. Do đó, chúng ta phải có các biện pháp để xử lý dòng chảy.

Xác thực từng phần dữ liệu đơn lẻ ' bằng tay 'suốt ngày đêm chỉ đơn giản là không thực tế. Thật là một giấc mơ tuyệt vời. Nhưng, suy cho cùng thì cũng chỉ có vậy thôi. Một giấc mơ. Dữ liệu xấu là dữ liệu xấu. Không cần biết bạn cắt hay cắt hạt lựu như thế nào (ý định chơi chữ). Đó là một vấn đề ngay từ đầu, dẫn đến nhiều vấn đề hơn nữa.

Cơ sở dữ liệu hiện đại xử lý nhiều công việc nặng nhọc cho chúng tôi. Nhiều giải pháp cung cấp các giải pháp tích hợp để hỗ trợ quản lý vùng dữ liệu cụ thể này.

Một cách chắc chắn để kiểm soát dữ liệu được nhập vào cột của bảng là sử dụng kiểu dữ liệu. Cần một cột có các số thập phân, có tổng số chữ số là 4, với 2 trong số các chữ số đó sau số thập phân?

Điều chắc chắn! Không có vấn đề gì cả.

NUMERIC (4,2), một lựa chọn khả thi, đang canh gác cột đó như một cơ quan giám sát. Giá trị văn bản ký tự có thể trượt vào đó không? Không phải là cơ hội của một quả cầu tuyết.

PostgreSQL cung cấp vô số kiểu dữ liệu. Rất có thể, một cái đã tồn tại để đáp ứng (các) nhu cầu của bạn. Nếu không, bạn có thể tạo của riêng bạn. (Xem:LOẠI TẠO PostgreSQL)

Tuy nhiên, chỉ các kiểu dữ liệu là không đủ. Bạn không thể đảm bảo các yêu cầu cụ thể nhất được bao phủ và tuân theo cấu trúc rộng như vậy. Các quy tắc tuân thủ và một số loại 'tiêu chuẩn' thường được yêu cầu khi thiết kế một lược đồ.

Giả sử trong cùng một cột SỐ (4,2) đó, bạn chỉ muốn các giá trị lớn hơn 25,25 nhưng nhỏ hơn 74,33? Trong trường hợp, giá trị 88,22 được lưu trữ, kiểu dữ liệu không bị lỗi. Bằng cách cho phép tổng cộng 4 chữ số, với nhiều nhất là 2 sau số thập phân, nó đang thực hiện công việc của mình. Đổ lỗi cho nơi khác.

Làm thế nào để chúng tôi giành chiến thắng trên mặt trận này khi kiểm soát dữ liệu được phép trong cơ sở dữ liệu của chúng tôi? Tính nhất quán của dữ liệu là ưu tiên hàng đầu và là không thể thiếu đối với bất kỳ giải pháp dữ liệu âm thanh nào. Nếu (tắt) nếu bạn kiểm soát dữ liệu được thu thập từ khi bắt đầu nguồn gốc của nó, tính nhất quán có thể sẽ ít gặp vấn đề hơn.

Nhưng, một thế giới hoàn hảo chỉ tồn tại (có thể) ở một trong số rất nhiều tiểu thuyết giả tưởng mà tôi thích đọc.

Rất tiếc, dữ liệu không đầy đủ, không nhất quán và 'bẩn' đều là những đặc điểm và thực tế quá phổ biến trong trường lấy cơ sở dữ liệu làm trung tâm.

Tuy nhiên, không phải tất cả đều chìm trong sự diệt vong và u ám vì chúng tôi có các ràng buộc Kiểm tra để giảm thiểu những vấn đề này. Đối với những quy tắc cụ thể đó, chúng tôi phải đưa ra, nếu cần thiết, để đảm bảo rằng chúng tôi chỉ xử lý và lưu trữ dữ liệu nhất quán. Bằng cách ủy thác các thông số kỹ thuật đó trong cơ sở dữ liệu, chúng tôi có thể giảm thiểu tác động của dữ liệu không nhất quán đối với các mục tiêu kinh doanh và giải pháp của chúng tôi trong tương lai.

Ràng buộc là gì? - Định nghĩa Cấp cao

Trong bối cảnh này, một ràng buộc là một loại quy tắc hoặc hạn chế được đặt trên một cột của bảng cơ sở dữ liệu. Tính cụ thể này yêu cầu dữ liệu đến phải tuân theo (các) yêu cầu đã đặt ra trước khi được lưu trữ. (Các) yêu cầu đã nói có xu hướng được đặt ra 'một cách chuyên nghiệp' (và thường là) như là các quy tắc kinh doanh . Điều này tổng hợp thành một thử nghiệm boolean xác thực cho sự thật. Nếu dữ liệu vượt qua (đúng), nó sẽ được lưu trữ. Nếu không, không có mục nào (sai).

Các ràng buộc có sẵn trong PostgreSQL

Tại thời điểm viết bài, tài liệu PostgreSQL liệt kê 6 loại ràng buộc.

Đó là:

  • Kiểm tra các ràng buộc
  • Ràng buộc Không Null
  • Ràng buộc Duy nhất
  • Khóa chính
  • Phím nước ngoài
  • Ràng buộc Loại trừ

Kiểm tra các ràng buộc

Một ví dụ đơn giản cho cột INTEGER là không cho phép các giá trị lớn hơn giả sử, 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).

Như đã thấy ở trên, các nỗ lực CHÈN bất kỳ giá trị nào vi phạm ràng buộc Kiểm tra đều không thành công.

Kiểm tra các ràng buộc không chỉ giám sát các cột trong khi INSERT, ngay cả các câu lệnh UPDATE (và các câu lệnh khác, ví dụ:\ copy và COPY) cũng phải tuân theo các hạn chế.

Giả sử bảng no_go có giá trị này:

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

CẬP NHẬT giá trị cột id thành một giá trị không phù hợp với ràng buộc Kiểm tra cũng không thành công:

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).

Ràng buộc kiểm tra phải 'có ý nghĩa' đối với kiểu dữ liệu cột mục tiêu. Việc cố gắng và ràng buộc cột INTEGER cấm lưu trữ các giá trị văn bản là không hợp lệ vì bản thân kiểu dữ liệu sẽ không cho phép điều đó.

Xem ví dụ này, nơi tôi cố gắng áp đặt loại ràng buộc Kiểm tra đó trong quá trình tạo bảng:

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

Cuộc sống không có ràng buộc kiểm tra

Một câu nói cũ mà tôi đã nghe rất vang vọng với tôi là:" Bạn đừng bỏ lỡ nước cho đến khi giếng cạn nước . "

Không có ràng buộc Kiểm tra, chúng tôi chắc chắn có thể liên quan vì lợi ích đáng kể của chúng được đánh giá cao nhất khi bạn phải làm mà không có chúng.

Lấy ví dụ này…

Để bắt đầu, chúng tôi có bảng này và dữ liệu đại diện cho vật liệu bề mặt đường mòn:

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)

Và bảng này với các tên đường và surface_id của riêng nó:

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)

Chúng tôi muốn đảm bảo rằng đường dẫn bảng chỉ chứa surface_id's cho các giá trị tương ứng trong table surface_material.

Ừ ừ tôi biết. Bạn đang hét vào mặt tôi.

" Điều này không thể được chăm sóc với a TỪ KHÓA NGOẠI TỆ?!? "

Có, nó có thể. Nhưng tôi đang sử dụng nó để chứng minh cách sử dụng chung, cùng với một cạm bẫy cần biết (được đề cập ở phần sau của bài đăng).

Không có ràng buộc Kiểm tra, bạn có thể sử dụng TRIGGER và ngăn các giá trị không nhất quán được lưu trữ.

Đây là một ví dụ thô (nhưng đang hoạt động):

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();

Cố gắng CHÈN một giá trị không có surface_id tương ứng trong đường dẫn bảng, không thành công:

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

Kết quả truy vấn bên dưới xác nhận ' vi phạm 'giá trị không được lưu trữ:

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)

Đó chắc chắn là rất nhiều công việc để cấm các giá trị không mong muốn.

Hãy thực hiện lại yêu cầu này với ràng buộc Kiểm tra.

Vì bạn không thể sử dụng truy vấn con (đây là lý do tại sao tôi sử dụng ví dụ trên) trong định nghĩa ràng buộc Kiểm tra thực tế, các giá trị phải được mã hóa cứng .

Đối với một bảng nhỏ, hoặc ví dụ nhỏ như được trình bày ở đây, điều này là tốt. Trong các trường hợp khác, kết hợp nhiều giá trị hơn, bạn có thể được phục vụ tốt hơn để tìm kiếm một giải pháp thay thế.

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

Ở đây tôi đã đặt tên cho ràng buộc Kiểm tra là t_check so với việc đặt tên cho hệ thống.

( Lưu ý:Đã xác định trước đó check_me () FUNCTION và đi kèm TRIGGER đã bị bỏ (không hiển thị) trước khi chạy phần bên dưới CHÈN.)

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).

Bạn có thấy điều đó dễ dàng như thế nào không! Không cần TRIGGER và FUNCTION.

Kiểm tra các ràng buộc làm cho loại công việc này trở nên dễ dàng.

Bạn muốn có được sự xảo quyệt trong định nghĩa ràng buộc Kiểm tra?

Bạn có thể.

Giả sử bạn cần một bảng liệt kê các đường mòn tử tế hơn một chút đối với những người có mắt cá chân và đầu gối nhạy cảm. Không có bề mặt cứng mong muốn ở đây.

Bạn muốn đảm bảo rằng bất kỳ đường mòn hoặc đường đi bộ đường dài nào được liệt kê trong bảng nice_trail đều có chất liệu bề mặt là 'Sỏi' hoặc 'Bụi bẩn'.

Ràng buộc Kiểm tra này xử lý yêu cầu đó không có vấn đề gì:

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

Điều đó hoàn toàn hoạt động tốt.

Nhưng, làm thế nào về một FUNCTION trả về cả hai id được yêu cầu để làm cho Kiểm tra hoạt động? Một FUNCTION có được phép trong định nghĩa ràng buộc Kiểm tra không?

Có, một cái có thể được kết hợp.

Đây là một ví dụ hoạt động.

Đầu tiên, phần thân hàm và định nghĩa:

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;

Lưu ý trong câu lệnh CREATE TABLE này, tôi xác định ràng buộc Kiểm tra tại ' bảng 'cấp trong khi trước đây tôi chỉ cung cấp các ví dụ tại' cột 'cấp.

Kiểm tra các ràng buộc được xác định ở cấp bảng là hoàn toàn hợp lệ:

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

Những phụ trang này đều tốt:

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

Bây giờ đi kèm với một INSERT cho đường nhỏ không đáp ứng hạn chế trên cột 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).

Lệnh gọi FUNCTION của chúng tôi trong định nghĩa ràng buộc Kiểm tra hoạt động như được thiết kế, hạn chế các giá trị cột không mong muốn.

Khói và Gương?

Mọi thứ có như vẻ ngoài với các ràng buộc Kiểm tra không? Tất cả màu đen và trắng? Không có mặt tiền ở phía trước?

Một ví dụ đáng lưu ý.

Chúng tôi có một bảng đơn giản, trong đó chúng tôi muốn giá trị DEFAULT là 10 cho cột INTEGER duy nhất hiện tại:

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

Tuy nhiên, tôi cũng đã bao gồm một ràng buộc Kiểm tra cấm giá trị là 10, bằng cách xác định id không thể bằng số đó.

Ai sẽ giành chiến thắng trong ngày? Ràng buộc DEFAULT hoặc Kiểm tra?

Bạn có thể ngạc nhiên khi biết nó là.

Tôi đã.

CHÈN tùy ý, hoạt động tốt:

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

Và một CHÈN với giá trị 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).

Rất tiếc ...

Một lần nữa, với cú pháp thay thế:

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

Ràng buộc Kiểm tra thắng giá trị DEFAULT.

Ví dụ về Oddball

Ràng buộc Kiểm tra có thể xuất hiện khá nhiều ở bất kỳ đâu trong định nghĩa bảng trong quá trình tạo. Ngay cả ở cấp độ cột, nó có thể được đặt trên một cột không liên quan đến việc kiểm tra bất kỳ điều gì.

Đây là một ví dụ để minh họa:

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

INSERT để kiểm tra ràng buộc:

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).

Hoạt động như dự định.

XÁC NHẬN và KHÔNG HỢP LỆ

Chúng tôi có bảng và dữ liệu đơn giản này:

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

Giả sử bây giờ chúng ta cần triển khai ràng buộc Kiểm tra cấm bất kỳ giá trị nào nhỏ hơn 50.

Hãy tưởng tượng đây là một bảng lớn đang được sản xuất và chúng tôi thực sự không thể mua được bất kỳ khóa nào có được vào lúc này, do một câu lệnh ALTER TABLE. Tuy nhiên, cần phải giải quyết ràng buộc này, tiến về phía trước.

ALTER TABLE sẽ nhận được một khóa (phụ thuộc vào từng biểu mẫu con khác nhau). Như đã đề cập, bảng này đang được sản xuất, vì vậy chúng tôi muốn đợi cho đến khi chúng tôi hết ' giờ cao điểm '.

Bạn có thể sử dụng tùy chọn KHÔNG HỢP LỆ khi tạo ràng buộc Kiểm tra:

learning=> ALTER TABLE v_check ADD CONSTRAINT fifty_chk CHECK(id > 50) NOT VALID; 
ALTER TABLE
Tải xuống Báo cáo chính thức hôm nay Quản lý &Tự động hóa PostgreSQL với ClusterControlTìm hiểu về những điều bạn cần biết để triển khai, giám sát, quản lý và mở rộng PostgreSQLTải xuống Báo cáo chính thức

Tiếp tục hoạt động, nếu cố gắng CHÈN hoặc CẬP NHẬT vi phạm ràng buộc Kiểm tra:

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).

Giá trị cột 'vi phạm' bị cấm.

Sau đó, trong thời gian ngừng hoạt động, chúng tôi xác thực ràng buộc Kiểm tra để áp dụng nó đối với (bất kỳ) cột tồn tại từ trước có thể vi phạm:

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

Tin nhắn khá khó hiểu theo quan điểm của tôi. Tuy nhiên, nó thông báo cho chúng ta biết rằng có những hàng không tuân theo ràng buộc.

Dưới đây là một số điểm chính mà tôi muốn đưa vào từ tài liệu ALTER TABLE (Verbiage trực tiếp từ tài liệu trong dấu ngoặc kép):

  • Cú pháp:ADD table_constraint [NOT VALID] - Mô tả kèm theo (một phần) "Biểu mẫu này thêm một ràng buộc mới vào một bảng bằng cú pháp tương tự như CREATE TABLE, cùng với tùy chọn NOT VALID, hiện chỉ được phép cho khóa ngoại và KIỂM TRA các ràng buộc. Nếu ràng buộc được đánh dấu là KHÔNG HỢP LỆ, thì quá trình kiểm tra ban đầu có thể kéo dài để xác minh rằng tất cả các hàng trong bảng thỏa mãn ràng buộc sẽ bị bỏ qua. "
  • Cú pháp:VALIDATE CONSTRAINT binding_name - Mô tả kèm theo (một phần) "Biểu mẫu này xác thực khóa ngoại hoặc kiểm tra ràng buộc đã được tạo trước đó là KHÔNG HỢP LỆ, bằng cách quét bảng để đảm bảo không có hàng nào mà ràng buộc không được thỏa mãn. " "Việc xác thực chỉ có được một khóa ĐỘC QUYỀN CHIA SẺ CẬP NHẬT trên bảng đang được thay đổi."

Ngoài ra, có hai điểm đáng chú ý mà tôi đã học được trong quá trình này. Các hàm trả về và truy vấn con không được phép trong Kiểm tra định nghĩa ràng buộc. Tôi chắc chắn rằng có những người khác và tôi hoan nghênh mọi phản hồi về họ trong phần bình luận bên dưới.

Ràng buộc kiểm tra là tuyệt vời. Sử dụng các giải pháp 'tích hợp sẵn' được cung cấp bởi chính cơ sở dữ liệu PostgreSQL, để thực thi (các) hạn chế dữ liệu là hoàn toàn hợp lý. Thời gian và nỗ lực dành cho việc triển khai Kiểm tra các ràng buộc đối với (các) cột cần thiết, vượt trội hơn nhiều so với việc không triển khai bất kỳ cột nào. Do đó tiết kiệm thời gian về lâu dài. Chúng ta càng dựa vào cơ sở dữ liệu để xử lý các loại yêu cầu này thì càng tốt. Cho phép chúng tôi tập trung và áp dụng các nguồn lực của mình vào các lĩnh vực / khía cạnh khác của quản lý cơ sở dữ liệu.

Cảm ơn bạn đã đọc.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Hướng tới Postgres-XL 9.5

  2. Giám sát cơ sở dữ liệu PostgreSQL:Mẹo để giám sát những gì

  3. Thay đổi loại cột và đặt không rỗng

  4. Cách chèn hàng loạt chỉ hàng mới trong PostreSQL

  5. Cách timeofday () hoạt động trong PostgreSQL