Điều đó là "dễ dàng" vì PostgreSQL rất có thể mở rộng. Bạn có thể xác định kiểu của riêng mình, các toán tử so sánh cho kiểu và một lớp toán tử để sử dụng với btree
lập chỉ mục để PostgreSQL biết cách so sánh chúng.
Bí quyết là xác định "bằng nhau" theo cách mà các giá trị xung đột là bằng nhau.
Đầu tiên, chúng tôi xác định loại của mình:
CREATE TYPE tod AS ENUM ('morning', 'afternoon', 'anytime');
Sau đó, chúng tôi xác định một quy trình hỗ trợ chỉ mục để btree
chỉ mục biết cách so sánh các giá trị:
CREATE FUNCTION tod_compare(tod, tod) RETURNS integer
IMMUTABLE LANGUAGE sql AS
$$SELECT CASE WHEN $1 = 'morning' AND $2 = 'afternoon' THEN -1
WHEN $1 = 'afternoon' AND $2 = 'morning' THEN 1
ELSE 0
END$$;
Dựa trên hàm so sánh này, chúng tôi xác định các hàm triển khai các toán tử so sánh:
CREATE FUNCTION tod_eq(tod, tod) RETURNS boolean IMMUTABLE LANGUAGE sql
AS 'SELECT tod_compare($1, $2) = 0';
CREATE FUNCTION tod_lt(tod, tod) RETURNS boolean IMMUTABLE LANGUAGE sql
AS 'SELECT tod_compare($1, $2) = -1';
CREATE FUNCTION tod_le(tod, tod) RETURNS boolean IMMUTABLE LANGUAGE sql
AS 'SELECT tod_compare($1, $2) <= 0';
CREATE FUNCTION tod_ge(tod, tod) RETURNS boolean IMMUTABLE LANGUAGE sql
AS 'SELECT tod_compare($1, $2) >= 0';
CREATE FUNCTION tod_gt(tod, tod) RETURNS boolean IMMUTABLE LANGUAGE sql
AS 'SELECT tod_compare($1, $2) = 1';
CREATE FUNCTION tod_ne(tod, tod) RETURNS boolean IMMUTABLE LANGUAGE sql
AS 'SELECT tod_compare($1, $2) <> 0';
Bây giờ chúng ta có thể xác định các toán tử trên kiểu của chúng ta:
CREATE OPERATOR ~=~ (
PROCEDURE = tod_eq,
LEFTARG = tod,
RIGHTARG = tod,
COMMUTATOR = ~=~,
NEGATOR = ~<>~
);
CREATE OPERATOR ~<>~ (
PROCEDURE = tod_ne,
LEFTARG = tod,
RIGHTARG = tod,
COMMUTATOR = ~<>~,
NEGATOR = ~=~
);
CREATE OPERATOR ~<=~ (
PROCEDURE = tod_le,
LEFTARG = tod,
RIGHTARG = tod,
COMMUTATOR = ~>=~,
NEGATOR = ~>~
);
CREATE OPERATOR ~<~ (
PROCEDURE = tod_lt,
LEFTARG = tod,
RIGHTARG = tod,
COMMUTATOR = ~>~,
NEGATOR = ~>=~
);
CREATE OPERATOR ~>~ (
PROCEDURE = tod_gt,
LEFTARG = tod,
RIGHTARG = tod,
COMMUTATOR = ~<~,
NEGATOR = ~<=~
);
CREATE OPERATOR ~>=~ (
PROCEDURE = tod_ge,
LEFTARG = tod,
RIGHTARG = tod,
COMMUTATOR = ~<=~,
NEGATOR = ~<~
);
Bây giờ tất cả những gì còn lại là xác định một lớp toán tử có thể được sử dụng để xác định một chỉ mục (điều này yêu cầu đặc quyền của người dùng cấp cao):
CREATE OPERATOR CLASS tod_ops DEFAULT FOR TYPE tod USING btree AS
OPERATOR 1 ~<~(tod,tod),
OPERATOR 2 ~<=~(tod,tod),
OPERATOR 3 ~=~(tod,tod),
OPERATOR 4 ~>=~(tod,tod),
OPERATOR 5 ~>~(tod,tod),
FUNCTION 1 tod_compare(tod,tod);
Bây giờ chúng ta có thể xác định một bảng sử dụng kiểu dữ liệu mới.
Vì chúng tôi đã xác định tod_ops
làm lớp toán tử mặc định cho loại tod
, chúng ta có thể tạo một ràng buộc duy nhất đơn giản và chỉ mục cơ bản sẽ sử dụng lớp toán tử của chúng ta.
CREATE TABLE schedule (
id integer PRIMARY KEY,
day date NOT NULL,
time_of_day tod NOT NULL,
UNIQUE (day, time_of_day)
);
Hãy kiểm tra nó:
INSERT INTO schedule VALUES (1, '2018-05-01', 'morning');
INSERT INTO schedule VALUES (2, '2018-05-01', 'afternoon');
INSERT INTO schedule VALUES (3, '2018-05-02', 'anytime');
INSERT INTO schedule VALUES (4, '2018-05-02', 'morning');
ERROR: duplicate key value violates unique constraint "schedule_day_time_of_day_key"
DETAIL: Key (day, time_of_day)=(2018-05-02, morning) already exists.
PostgreSQL có hay không?