Ограничение Postgres - PullRequest
       19

Ограничение Postgres

0 голосов
/ 08 мая 2018

Есть ли способ в postgres создать ограничение, которое работает так:

У меня есть объект со значением "time_of_day". Это значение может быть утром, днем, вечером, днем, ночью или в любое время.

ТАК Я пытаюсь выяснить, как разрешить следующие комбинации:

  1. В любое время (больше ничего не может), т. Е. Может быть только одна строка, если выбрано любое время
  2. Утро или День - может быть много строк, но ни одна не может содержать «В любое время». Также не может быть двух строк одного типа, например два «утренних» ряда.

(2) сделано, так как это просто стандартное уникальное ограничение для time_of_day. Как мне достичь (1). Возможно ли это?

1 Ответ

0 голосов
/ 08 мая 2018

Это & ldquo; easy & rdquo; потому что PostgreSQL настолько расширяем. Вы можете определить свой собственный тип, операторы сравнения для типа и класс операторов для использования с индексом btree, чтобы PostgreSQL знал, как их сравнивать.

Хитрость в том, чтобы определить & ldquo; равный & rdquo; таким образом, что конфликтующие значения равны.

Сначала мы определим наш тип:

CREATE TYPE tod AS ENUM ('morning', 'afternoon', 'anytime');

Затем мы определяем подпрограмму поддержки индекса , чтобы индекс btree знал, как сравнивать значения:

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 не крут?

...