Ограничения и утверждения в PostgreSQL - PullRequest
4 голосов
/ 11 марта 2012

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

CREATE TABLE CUSTOMER
(
    CUSTOMER_NUM CHAR(3) PRIMARY KEY,
    CUSTOMER_NAME CHAR(35) NOT NULL,
    STREET CHAR(15),
    CITY CHAR(15),
    STATE CHAR(3),
    ZIP CHAR(5),
);
CREATE TABLE ORDERS
(
    ORDER_NUM CHAR(5) PRIMARY KEY,
    ORDER_DATE DATE,
    CUSTOMER_NUM CHAR(3),

    CONSTRAINT CUSTOMER_NUM_FKEY FOREIGN KEY (CUSTOMER_NUM)
        REFRENCES CUSTOMER (CUSTOMER_NUM) MATCH SIMPLE
        ON UPDATE CASCADE ON DELETE CASCADE 
);

И это то, что я написал, чтобы применить это ограничение, но оно не работает.Я предполагаю, потому что ORDER_NUM и ORDER_DATE никогда не имеют равных значений.

CREATE ASSERTION ITEM_LIMIT
CEHCK(
        (   SELECT COUNT(*)
            FROM CUSTOMER C1, ORDERS O1
            WHERE C1.CUSTOMER_NUM = O1.CUSTOMER_NUM AND
                O1.ORDER_DATE = O1.ORDER_NUM
     ) <= 1000

Мой вопрос заключается в том, как заставить работать это ограничение, например, как я ограничиваю количество заказов в день.

Ответы [ 2 ]

7 голосов
/ 11 марта 2012

Поскольку @ruakh уже выяснил, в PostgreSQL нет CREATE ASSERTION. Просто проверьте список команд SQL . Его там нет.

Вы можете использовать триггеры, которые обновляют счет на клиента в сочетании с ограничением CHECK, но вы должны охватывать все соответствующие операторы DML: INSERT, UPDATE, DELETE. Может выглядеть так:

Подготовить существующую таблицу клиентов:

ALTER TABLE customer ADD COLUMN order_ct integer DEFAULT 0;
UPDATE customer SET order_ct = 0;
ALTER TABLE customer ALTER order_ct SET NOT NULL;
ALTER TABLE customer ADD CONSTRAINT order_ct_max1000 CHECK (order_ct <= 1000);

Создание триггерных функций и триггеров:

CREATE OR REPLACE FUNCTION trg_order_upaft()
  RETURNS trigger AS
$BODY$
BEGIN

IF OLD.customer_num <> NEW.customer_num THEN
    UPDATE customer
    SET    order_ct = order_ct - 1
    WHERE  customer_num = OLD.customer_num;

    UPDATE customer
    SET    order_ct = order_ct + 1
    WHERE  customer_num = NEW.customer_num;
END IF;

RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql;

CREATE TRIGGER upaft
  AFTER UPDATE ON orders FOR EACH ROW
  EXECUTE PROCEDURE trg_order_upaft();


CREATE OR REPLACE FUNCTION trg_order_insaft()
  RETURNS trigger AS
$BODY$
BEGIN

UPDATE customer
SET    order_ct = order_ct + 1
WHERE  customer_num = NEW.customer_num;

RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql;

CREATE TRIGGER insaft
  AFTER INSERT ON orders FOR EACH ROW
  EXECUTE PROCEDURE trg_order_insaft();


CREATE OR REPLACE FUNCTION trg_order_delaft()
  RETURNS trigger AS
$BODY$
BEGIN

UPDATE customer
SET    order_ct = order_ct - 1;
WHERE  customer_num = OLD.customer_num;

RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql;

CREATE TRIGGER delaft
  AFTER DELETE ON orders FOR EACH ROW
  EXECUTE PROCEDURE trg_order_delaft();

Я сделал все эти триггеры ПОСЛЕ триггеров - вот почему все в порядке с RETURN NULL. AFTER предпочтительнее ДО в этом случае. Он работает лучше, если какие-либо другие условия могут отменить операторы DML в середине (как и другие триггеры).

Если у вас нет ничего подобного, то ПЕРЕД триггерами может быть предпочтительнее. В этом случае убедитесь, что функции триггера ВЕРНУТЫ НОВЫЙ / СТАРЫЙ соответственно.

3 голосов
/ 11 марта 2012

Я не верю, что PostgreSQL применяет операторы CREATE ASSERTION;по крайней мере, «Утверждения» перечислены как неподдерживаемая функция в Приложении D.2 к Руководству по PostgreSQL .Насколько я знаю, на самом деле нет основных СУБД обеспечивает их применение.

Решение состоит в том, чтобы вместо этого использовать триггер ;Вы можете настроить его на запуск перед любыми вставками в ORDERS и выдавать ошибку, если обнаружит эту проблему.(Я предполагаю, что update на ORDERS никогда не вызовет этой проблемы, но если они могут, то вам также потребуется триггер для этого случая.)

...