PostgreSQL, триггеры и параллелизм для принудительного использования временного ключа - PullRequest
1 голос
/ 09 июня 2010

Я хочу определить триггер в PostgreSQL, чтобы проверить, что у вставленной строки в общей таблице есть свойство: «не существует другой строки с таким же ключом в то же действительное время» (ключи являются последовательными ключами) , На самом деле я это уже реализовал. Но так как триггер должен сканировать всю таблицу, теперь мне интересно: есть ли необходимость в блокировке на уровне таблицы? Или это каким-то образом управляется самим PostgreSQL?

Вот пример. В следующем PostgreSQL 9.0 я бы определил таблицу следующим образом:

CREATE TABLE medicinal_products
(
aic_code CHAR(9), -- sequenced key
full_name VARCHAR(255),
market_time PERIOD,
    EXCLUDE USING gist
    (aic_code CHECK WITH =,
    market_time CHECK WITH &&)
);

но на самом деле я был определен так:

CREATE TABLE medicinal_products
(
PRIMARY KEY (aic_code, vs),
aic_code CHAR(9), -- sequenced key
full_name VARCHAR(255),
vs DATE NOT NULL,
ve DATE,
CONSTRAINT valid_time_range
        CHECK (ve > vs OR ve IS NULL)
);

Затем я написал триггер, который проверяет соотношение затрат: «Два разных лекарственных средства могут иметь один и тот же код в два разных периода, но не в одно и то же время».

Итак, код:

INSERT INTO medicinal_products VALUES ('1','A','2010-01-01','2010-04-01');
INSERT INTO medicinal_products VALUES ('1','A','2010-03-01','2010-06-01');

вернуть ошибку.

Ответы [ 3 ]

1 голос
/ 09 июня 2010

Одним из решений является наличие второй таблицы для обнаружения столкновений и ее заполнение триггером. Используя схему, которую вы добавили в вопрос:

CREATE TABLE medicinal_product_date_map(
   aic_code char(9) NOT NULL,
   applicable_date date NOT NULL,
   UNIQUE(aic_code, applicable_date));

(примечание: это вторая попытка из-за неправильного прочтения вашего требования в первый раз. Надеюсь, на этот раз это правильно).

Некоторые функции для ведения этой таблицы:

CREATE FUNCTION add_medicinal_product_date_range(aic_code_in char(9), start_date date, end_date date)
RETURNS void STRICT VOLATILE LANGUAGE sql AS $$
  INSERT INTO medicinal_product_date_map
  SELECT $1, $2 + offset
  FROM generate_series(0, $3 - $2)
$$;
CREATE FUNCTION clr_medicinal_product_date_range(aic_code_in char(9), start_date date, end_date date)
RETURNS void STRICT VOLATILE LANGUAGE sql AS $$
  DELETE FROM medicinal_product_date_map
  WHERE aic_code = $1 AND applicable_date BETWEEN $2 AND $3
$$;

И заполните таблицу в первый раз:

SELECT count(add_medicinal_product_date_range(aic_code, vs, ve))
FROM medicinal_products;

Теперь создайте триггеры для заполнения карты дат после изменений в medicinal_products: после вызовов вставки add_, после вызовов вызова update clr_ (старые значения) и add_ (новые значения), после вызовов delete clr _.

CREATE FUNCTION sync_medicinal_product_date_map()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
  IF TG_OP = 'UPDATE' OR TG_OP = 'DELETE' THEN
    PERFORM clr_medicinal_product_date_range(OLD.aic_code, OLD.vs, OLD.ve);
  END IF;
  IF TG_OP = 'UPDATE' OR TG_OP = 'INSERT' THEN
    PERFORM add_medicinal_product_date_range(NEW.aic_code, NEW.vs, NEW.ve);
  END IF;
  RETURN NULL;
END;
$$;
CREATE TRIGGER sync_date_map
  AFTER INSERT OR UPDATE OR DELETE ON medicinal_products
  FOR EACH ROW EXECUTE PROCEDURE sync_medicinal_product_date_map();

Ограничение уникальности medicinal_product_date_map будет перехватывать любые товары, добавляемые с тем же кодом в тот же день:

steve@steve@[local] =# INSERT INTO medicinal_products VALUES ('1','A','2010-01-01','2010-04-01');
INSERT 0 1
steve@steve@[local] =# INSERT INTO medicinal_products VALUES ('1','A','2010-03-01','2010-06-01');
ERROR:  duplicate key value violates unique constraint "medicinal_product_date_map_aic_code_applicable_date_key"
DETAIL:  Key (aic_code, applicable_date)=(1        , 2010-03-01) already exists.
CONTEXT:  SQL function "add_medicinal_product_date_range" statement 1
SQL statement "SELECT add_medicinal_product_date_range(NEW.aic_code, NEW.vs, NEW.ve)"
PL/pgSQL function "sync_medicinal_product_date_map" line 6 at PERFORM

Это зависит от того, какие значения проверяются на наличие дискретного пространства, поэтому я спросил о датах и ​​временных отметках. Хотя временные метки технически являются дискретными, поскольку Postgresql хранит только микросекундное разрешение, добавление записи в таблицу карт для каждой микросекунды, к которой применяется продукт, нецелесообразно.

Сказав это, вы, вероятно, могли бы также воспользоваться чем-то лучшим, чем сканирование полной таблицы, чтобы проверить перекрывающиеся интервалы временных отметок, с некоторой хитростью при поиске только первого интервала не после или не раньше ... однако, для Простые дискретные пробелы. Я предпочитаю такой подход, который IME также может пригодиться и для других вещей (например, отчеты, которые должны быстро найти, какие продукты применимы в определенный день).

Мне также нравится этот подход, потому что он считает правильным использовать механизм ограничения уникальности базы данных таким образом. Кроме того, я чувствую, что он будет более надежным в контексте одновременных обновлений главной таблицы: без блокировки таблицы от одновременных обновлений будет возможно для триггера проверки не видеть конфликта и разрешать вставки в двух одновременных сеансах, которые затем конфликтует, когда видны эффекты обеих транзакций.

0 голосов
/ 09 июня 2010

Почему вы не можете использовать УНИКАЛЬНОЕ ограничение? Будет намного быстрее (это индекс) и проще.

0 голосов
/ 09 июня 2010

Просто подумайте, в случае, если действительные временные блоки могут быть закодированы числом или чем-то еще, создание уникального индекса на Id + TimeBlock будет невероятно быстрым и решит все проблемы с блокировкой таблицы.самим PostgreSQL.Для выбора он получает блокировку ACCESS_SHARE, что означает, что вы можете запрашивать таблицу, но не выполнять обновления.

Радикальное решение, которое может помочь вам, - это использовать кеш, такой как ehcache или memcached, для хранения информации id / timeblock и вообще не использовать postgresql.Многие из них можно сохранить, чтобы они могли выдержать перезапуск сервера и не проявляют такого поведения блокировки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...