Това е „лесно“, защото PostgreSQL е толкова разширим. Можете да дефинирате свой собствен тип, оператори за сравнение за типа и клас оператори, които да използвате с btree
индекс, така че PostgreSQL да знае как да ги сравни.
Номерът е да дефинирате „равен“ по такъв начин, че конфликтните стойности да са равни.
Първо дефинираме нашия тип:
CREATE TYPE tod AS ENUM ('morning', 'afternoon', 'anytime');
След това дефинираме програма за поддръжка на индекс така че btree
index знае как да сравнява стойностите:
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$$;
Въз основа на тази функция за сравнение ние дефинираме функции, които прилагат операторите за сравнение:
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';
Сега можем да дефинираме оператори за нашия тип:
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 = ~<~
);
Сега всичко, което остава, е да дефинираме операторен клас който може да се използва за дефиниране на индекс (това изисква привилегии на суперпотребител):
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);
Сега можем да дефинираме таблица, която използва новия тип данни.
Тъй като дефинирахме tod_ops
като операторен клас по подразбиране за тип tod
, можем да създадем просто уникално ограничение и основният индекс ще използва нашия операторен клас.
CREATE TABLE schedule (
id integer PRIMARY KEY,
day date NOT NULL,
time_of_day tod NOT NULL,
UNIQUE (day, time_of_day)
);
Нека го тестваме:
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 не е ли страхотен?