Itu "mudah" karena PostgreSQL sangat dapat diperluas. Anda dapat menentukan tipe Anda sendiri, operator perbandingan untuk tipe dan kelas operator yang akan digunakan dengan btree
indeks sehingga PostgreSQL tahu cara membandingkannya.
Triknya adalah dengan mendefinisikan "sama" sedemikian rupa sehingga nilai-nilai yang bertentangan adalah sama.
Pertama, kita mendefinisikan tipe kita:
CREATE TYPE tod AS ENUM ('morning', 'afternoon', 'anytime');
Kemudian kami mendefinisikan rutin dukungan indeks sehingga btree
index tahu cara membandingkan nilai:
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$$;
Berdasarkan fungsi perbandingan ini, kami mendefinisikan fungsi yang mengimplementasikan operator perbandingan:
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';
Sekarang kita dapat mendefinisikan operator pada tipe kita:
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 = ~<~
);
Sekarang yang tersisa hanyalah mendefinisikan kelas operator yang dapat digunakan untuk mendefinisikan indeks (ini memerlukan hak pengguna super):
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);
Sekarang kita dapat mendefinisikan tabel yang menggunakan tipe data baru.
Karena kita mendefinisikan tod_ops
sebagai kelas operator default untuk tipe tod
, kita dapat membuat batasan unik yang sederhana, dan indeks yang mendasarinya akan menggunakan kelas operator kita.
CREATE TABLE schedule (
id integer PRIMARY KEY,
day date NOT NULL,
time_of_day tod NOT NULL,
UNIQUE (day, time_of_day)
);
Mari kita uji:
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.
Bukankah PostgreSQL keren?