PostgreSQL中的约束和断言

ges*_*alt 4 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 
);
Run Code Online (Sandbox Code Playgroud)

这就是我为强制执行此约束而编写的内容,但它不起作用.我假设它是因为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
Run Code Online (Sandbox Code Playgroud)

我的问题是如何使这个约束起作用,比如如何限制每天的订单量.

Erw*_*ter 7

由于@ruakh已经清理完毕,CREATE ASSERTIONPostgreSQL中没有.只需检查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);
Run Code Online (Sandbox Code Playgroud)

创建触发器函数和触发器:

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();
Run Code Online (Sandbox Code Playgroud)

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

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


rua*_*akh 5

我不相信 PostgreSQL 强制执行CREATE ASSERTION语句;至少,“断言”在PostgreSQL 手册的附录 D.2中被列为不支持的功能。据我所知,实际上,没有一个主要的 DBMS 强制执行它们。

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

  • 真的吗?因为这就是我们被教导如何做出断言的方式,而且我们正在使用 postgreSQL。 (2认同)