Внешний ключ POSTGRESQL как ограничение - PullRequest
0 голосов
/ 12 декабря 2011

У меня есть таблица A с именем Order

. Она имеет PK id , несколько столбцов, столбец bool с именем active и столбец tableid .Два ордера не могут быть активными одновременно для tableid , который обеспечивается уникальным противопоказанием из двух столбцов (&& active = true), которое работает как индекс, и поиск активных ордеров выполняется довольно быстро.
проблема в том, что есть еще одна таблица orderitems .Я хочу, чтобы активным было значение true, если все элементы заказа для идентификатора заказа не помечены как оплаченные = true ..
С помощью транзакций сериализации этого можно достичь, я думаю, в коде платежа, задав запрос на обновление, если все товары оплачены.Я думаю, что это не будет работать всегда. Потому что, если они запускаются одновременно, они оба могут видеть, что есть неоплаченные элементы (из-за старого снимка), но при фиксации они будут платить все элементы, но не обновлять активный столбец. (Другой).
При добавлении новых элементов и платежной транзакции выполняется попытка установить активное = ложное, не будет проблемы с последовательными транзакциями, потому что одна из них потерпит неудачу.
Я думаю, что триггеры - это решение, но я не знаю, что именно делать. Спасибо за чтение.

1 Ответ

2 голосов
/ 13 декабря 2011

Что вы хотите сделать, это добавить триггер AFTER UPDATE OR INSERT OR DELETE FOR EACH ROW на orderitems, который определяет, следует ли изменить Order.active. Вам нужно будет сделать SELECT ... FOR UPDATE в строке Order, которой принадлежат эти orderitems, в противном случае вы рискуете одновременным запуском триггерных гонок друг против друга и обновлением не по порядку.

Предполагая, что orderitems имеет поле order_id, которое является ссылкой вашего внешнего ключа на Order.id, попробуйте что-то вроде (непроверенного, только для общего примера) кода, следующего:

CREATE OR REPLACE FUNCTION orderitems_order_active_trigger() RETURNS trigger AS $$
DECLARE
  _old_active BOOLEAN;
  _new_active BOOLEAN;
  _order_id INTEGER;
BEGIN
  IF tg_op = 'INSERT' THEN
    _order_id = NEW.order_id;
  ELIF tg_op = 'UPDATE' THEN
    _order_id = NEW.order_id;
  ELIF tg_op = 'DELETE' THEN
    _order_id = OLD.order_id;
  ELSE
    RAISE EXCEPTION 'Unexpected trigger operation %',tg_op;
  END IF;
  -- Lock against concurrent trigger runs and other changes in the parent record while
  -- obtaining the current value of `active`
  SELECT INTO _old_active Order.active FROM Order WHERE Order.id = _order_id FOR UPDATE;
  -- Now figure out whether the order should be active. We'll say that if there are
  -- more than zero unpaid order items found we'll treat it as active.
  _new_active = EXISTS(SELECT 1 FROM orderitems WHERE orderitems.order_id = _order_id AND orderitems.paid='f');
  -- If the active state has flipped, update the order.
  IF _old_active IS DISTINCT FROM _new_active THEN
    UPDATE Order SET active = _new_active WHERE Order.id = _order_id;
  END IF;
END;
$$ LANGUAGE 'plpgsql' VOLATILE;

CREATE TRIGGER orderitems_ensure_active_correct AFTER INSERT OR UPDATE OR DELETE 
ON orderitems FOR EACH ROW EXECUTE PROCEDURE orderitems_order_active_trigger();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...