Giới thiệu
PostgreSQL nguyên bản cung cấp nhiều kiểu dữ liệu phong phú hỗ trợ nhiều trường hợp sử dụng thực tế. Bài viết này giới thiệu cách triển khai đặc biệt của các kiểu dữ liệu nối tiếp thường được sử dụng để tạo khóa chính tổng hợp.
Các phím duy nhất
Giới luật cơ bản của lý thuyết thiết kế cơ sở dữ liệu là mỗi bộ (tức là hàng) của một quan hệ (tức là bảng) phải được xác định duy nhất từ các bộ khác. Các thuộc tính hoặc cột, cùng nhau xác định rõ ràng một bộ từ tất cả các bộ khác được gọi là "khóa". Một số người theo chủ nghĩa thuần túy cho rằng bất kỳ đối tượng hoặc khái niệm được mô hình hóa nào đều sở hữu một thuộc tính hoặc tập hợp các thuộc tính có thể đóng vai trò là khóa và điều quan trọng là phải xác định tập hợp các thuộc tính chính này và sử dụng chúng để lựa chọn các bộ giá trị duy nhất.
Nhưng như một vấn đề thực tế, việc xác định một tập hợp đủ lớn các thuộc tính đảm bảo tính duy nhất cho một đối tượng được mô hình hóa có thể không thực tế và do đó, đối với việc triển khai trong thế giới thực, các nhà phát triển thường chuyển sang các khóa tổng hợp như một vật thay thế. Nghĩa là, thay vì dựa vào một số kết hợp các thuộc tính thực tế, một giá trị bên trong cơ sở dữ liệu, thường là các giá trị số nguyên tăng dần và không có ý nghĩa vật lý nào được định nghĩa là khóa. Ngoài sự đơn giản của một khóa cột duy nhất, thực tế là không có sự phụ thuộc trong thế giới thực có nghĩa là các yếu tố bên ngoài không bao giờ có thể buộc phải thay đổi giá trị, chẳng hạn như, có thể là trường hợp nếu tên của một người được sử dụng như một chìa khóa ... và sau đó người đó kết hôn hoặc tham gia chương trình bảo vệ nhân chứng của chính phủ liên bang và đổi tên của họ. Ngay cả một số giá trị thường được cư dân cho là duy nhất và bất biến, chẳng hạn như số an sinh xã hội của Hoa Kỳ, cũng không:một người có thể lấy SSN mới và SSN đôi khi được sử dụng lại.
Khai báo kiểu dữ liệu nối tiếp
PostgreSQL cung cấp một khai báo kiểu dữ liệu đặc biệt để đáp ứng nhu cầu về khóa tổng hợp này. Khai báo cột trong bảng cơ sở dữ liệu là kiểu SERIAL đáp ứng yêu cầu về khóa tổng hợp bằng cách cung cấp các số nguyên duy nhất khi chèn các bộ giá trị mới. Kiểu dữ liệu giả này thực hiện một cột kiểu dữ liệu số nguyên với một giá trị mặc định liên quan được dẫn xuất thông qua một lệnh gọi hàm cung cấp các giá trị số nguyên tăng dần. Thực thi đoạn mã sau để tạo một bảng đơn giản với cột id kiểu serial:
CREATE TABLE person (id serial, full_name text);
actually executes the following DDL
CREATE TABLE person (
id integer NOT NULL,
full_name text
);
CREATE SEQUENCE person_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE person_id_seq OWNED BY person.id;
ALTER TABLE ONLY person
ALTER COLUMN id
SET DEFAULT nextval('person_id_seq'::regclass);
Nghĩa là, từ khóa "serial" như một đặc tả kiểu dữ liệu ngụ ý thực thi các câu lệnh DDL tạo ra một cột kiểu số nguyên với ràng buộc NOT NULL, một SEQUENCE, và sau đó mặc định cột là ALTERED để gọi một hàm tích hợp đang truy cập vào SEQUENCE đó.
Hàm tích hợp nextval thực hiện một dịch vụ tự động tăng:mỗi lần gọi giá trị tiếp theo, nó sẽ tăng bộ đếm trình tự được chỉ định và trả về giá trị mới tăng.
Bạn có thể xem kết quả của hiệu ứng này bằng cách kiểm tra định nghĩa bảng:
postgres=# \d person
Table "public.person"
Column | Type | Modifiers
-----------+---------+-----------------------------------------
Id | integer | not null default nextval('person_id_seq'::regclass)
full_name | text |
Chèn giá trị sê-ri
Để sử dụng chức năng tăng tự động, chúng tôi chỉ cần chèn các hàng, dựa vào giá trị mặc định cho cột nối tiếp:
INSERT INTO person (full_name) VALUES ('Alice');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
(1 row)
Chúng tôi thấy rằng giá trị cho cột id tương ứng với hàng “Alice” mới đã được tạo tự động. Ngoài ra, người ta có thể sử dụng từ khóa DEFAULT nếu muốn liệt kê rõ ràng tất cả các tên cột:
INSERT INTO person (id, full_name) VALUES (DEFAULT, 'Bob');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
(2 rows)
nơi chúng tôi thấy chức năng tự động tăng dần rõ ràng hơn, gán giá trị nối tiếp-tiếp theo cho hàng mới cho phần chèn thứ hai của “Bob”.
Chèn nhiều hàng thậm chí hoạt động:
INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
(4 rows)
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 Thiếu giá trị sê-ri
Chức năng nextval tích hợp được tối ưu hóa cho các ứng dụng không chặn, có tính đồng thời cao và do đó không tôn trọng việc khôi phục. Do đó, điều này có nghĩa là có thể thiếu các giá trị trong chuỗi. Dưới đây, chúng tôi khôi phục một chèn, nhưng sau đó thấy một chèn tiếp theo nhận một giá trị mới bỏ qua giá trị mà lẽ ra đã được liên kết với giao dịch bị hủy bỏ:
BEGIN TRANSACTION;
INSERT INTO person (full_name) VALUES ('Eve');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
5 | Eve
(5 rows)
ROLLBACK;
INSERT INTO person (full_name) VALUES ('Fred');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
6 | Fred
(5 rows)
Ưu điểm của việc không tôn trọng quá trình khôi phục là các phiên khác cố gắng chèn đồng thời sẽ không bị chặn bởi các phiên chèn khác.
Một cách khác để kết thúc với các giá trị bị thiếu là nếu các hàng bị xóa:
DELETE FROM person WHERE full_name = 'Cathy';
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
6 | Fred
(4 rows)
Lưu ý rằng ngay cả sau khi xóa hàng được chèn gần đây nhất tương ứng với giá trị cột id tăng tự động lớn nhất, bộ đếm trình tự không hoàn nguyên, tức là ngay cả sau khi xóa hàng tương ứng với 'Fred', đối với các lần chèn tiếp theo, bộ đếm trình tự vẫn giữ nguyên giá trị lớn nhất đã biết trước đây và số gia tăng từ đó:
DELETE FROM person WHERE full_name = 'Fred';
INSERT INTO person (full_name) VALUES ('Gina');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
(4 rows)
Khoảng trống hoặc các giá trị bị thiếu như được hiển thị ở trên được một số nhà phát triển ứng dụng xem là vấn đề vì trong danh sách gửi thư chung của PostgreSQL, có một lời nhắc lại chậm nhưng ổn định về câu hỏi làm thế nào để tránh khoảng trống trình tự khi sử dụng kiểu dữ liệu giả nối tiếp. Đôi khi không có yêu cầu kinh doanh cơ bản thực tế nào, đó chỉ là vấn đề cá nhân chán ghét các giá trị còn thiếu. Nhưng có những trường hợp mà việc ngăn chặn các con số bị thiếu là một nhu cầu thực sự và đó là chủ đề cho một bài viết tiếp theo.
KHÔNG BẠN KHÔNG THỂ - CÓ BẠN CÓ THỂ!
Ràng buộc NOT NULL do kiểu dữ liệu giả nối tiếp đưa ra bảo vệ chống lại việc chèn NULL cho cột id bằng cách từ chối các nỗ lực chèn như vậy:
INSERT INTO person (id, full_name) VALUES (NULL, 'Henry');
ERROR: null value in column "id" violates not-null constraint
DETAIL: Failing row contains (null, Henry).
Do đó, chúng tôi yên tâm về việc có giá trị cho thuộc tính đó.
Tuy nhiên, một vấn đề mà một số người gặp phải là, như đã khai báo ở trên, không có gì ngăn cản việc chèn các giá trị một cách rõ ràng, bỏ qua giá trị tự động tăng thêm mặc định có được thông qua việc gọi hàm nextval:
INSERT INTO person (id, full_name) VALUES (9, 'Ingrid');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
9 | Ingrid
(5 rows)
Nhưng sau đó hai lần chèn sau đó bằng cách sử dụng mặc định sẽ tạo ra một giá trị trùng lặp cho cột id nếu không có ràng buộc kiểm tra các giá trị cột so với giá trị trình tự:
INSERT INTO person (full_name) VALUES ('James');
INSERT INTO person (full_name) VALUES ('Karen');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
9 | Ingrid
8 | James
9 | Karen
(7 rows)
Nếu trên thực tế, chúng tôi đang sử dụng cột id nối tiếp làm khóa, chúng tôi sẽ khai báo nó là một KHÓA CHÍNH hoặc ít nhất là tạo một CHỈ SỐ DUY NHẤT. Nếu chúng tôi làm điều đó, thì chèn 'Karen' ở trên sẽ không thành công với lỗi khóa trùng lặp. Bản phát hành gần đây nhất của PostgreSQL bao gồm cú pháp khai báo ràng buộc mới 'được tạo theo mặc định làm danh tính' để tránh lỗi này và một số vấn đề cũ khác liên quan đến kiểu dữ liệu giả nối tiếp.
Các chức năng thao tác trình tự
Ngoài hàm nextval mà chúng ta đã đề cập để nâng cao trình tự và trả về giá trị mới, có một số hàm khác để truy vấn và đặt giá trị trình tự:hàm currval trả về giá trị thu được gần đây nhất với giá trị tiếp theo cho chuỗi đã chỉ định, hàm lastval trả về giá trị thu được gần đây nhất với giá trị tiếp theo cho bất kỳ trình tự nào và hàm setval đặt giá trị hiện tại của trình tự. Các hàm này được gọi bằng các truy vấn đơn giản., Chẳng hạn như
SELECT currval('person_id_seq');
currval
---------
9
(1 row)
Và lưu ý rằng nếu một lệnh gọi đến hàm nextval độc lập với việc thực sự thực hiện một đoạn chèn, nó sẽ làm tăng trình tự và điều đó sẽ được phản ánh trong các lần chèn tiếp theo:
SELECT nextval('person_id_seq');
nextval
---------
10
(1 row)
INSERT INTO person (full_name) VALUES ('Larry');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
7 | Gina
9 | Ingrid
8 | James
9 | Karen
11 | Larry
(8 rows)
Kết luận
Chúng tôi đã giới thiệu những hiểu biết cơ bản về kiểu dữ liệu giả PostgreSQL SERIAL cho các khóa tổng hợp tự động tăng dần. Để minh họa trong bài viết này, chúng tôi đã sử dụng khai báo kiểu SERIAL, tạo ra một cột số nguyên 4 byte. PostgreSQL đáp ứng các nhu cầu phạm vi khác nhau với kiểu dữ liệu giả SMALLSERIAL và BIGSERIAL cho kích thước cột 2 byte và 8 byte tương ứng. Hãy tìm một bài báo trong tương lai về một phương tiện giải quyết nhu cầu về trình tự không bị thiếu giá trị.