PostgreSQL中的约束和断言

时间:2012-03-11 17:15:41

标签: sql postgresql triggers constraints plpgsql

我正在尝试创建一个简单的数据库,其中包含客户数据表和订单数据表。我正在尝试编写一个约束,使得客户无法在特定日期订购超过特定数量的商品。这就是我所拥有的:

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 个答案:

答案 0 :(得分:7)

由于@ruakh已经清理完毕,PostgreSQL中没有CREATE ASSERTION。只需查看list of SQL commands即可。它不存在。

您可以使用更新每个客户的计数并结合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();

我在AFTER触发后创造了所有这些触发器 - 这就是RETURN NULL的原因。在这种情况下,优先于BEFORE。如果任何其他条件可以取消中间的DML语句(如其他触发器),它会表现得更好。

如果您没有任何类型,那么BEFORE触发器可能更可取。在这种情况下,请务必使触发功能相应地返回NEW / OLD。

答案 1 :(得分:3)

我不相信PostgreSQL强制执行CREATE ASSERTION语句;至少,“断言”在Appendix D.2 of the PostgreSQL Manual中列为不受支持的功能。据我所知,实际上,主要DBMS的 none 强制执行它们。

解决方案是使用trigger代替;您可以将其设置为在ORDERS上的任何插入之前运行,并在检测到此问题时引发错误。 (我假设ORDERS上的更新永远不会引入这个问题,但如果可以的话,那么你也需要触发这个案例。)