2012-03-11 249 views
1

我想创建一个简单的数据库,其中有一张客户数据表和一张订单数据表。我正在尝试编写一个约束条件,以便客户在特定日期内不能订购超过特定数量的商品。下面是我有:PostgreSQL中的约束和断言

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 

我的问题是如何让这个约束工作,就像我如何限制每天的订单数量。

+0

声明不是[postgresql的保留关键字](http://www.postgresql.org/docs/9.1/interactive/sql-keywords-appendix.html),尽管它适用于sql-99和92。 – 2012-03-11 18:26:50

+1

@Clodoaldo:这是无关紧要的。这是PostgreSQL中的关键词,它不是*保留*关键词;这意味着尽管'ASSERTION'被理解为像CREATE ASSERTION ...这样的上下文中的关键词,它可以被用作标识符(所以像'CREATE TABLE assertion ...'是允许的)。 PostgreSQL不接受CREATE ASSERTION语句,它只是不执行它们描述的断言。 – ruakh 2012-03-11 19:45:33

+0

@ruakh如果你试图在PostgreSQL中创建一个断言(至少9.4),你会得到'错误:CREATE ASSERTION还没有实现'。所以我会说它不接受他们。你描述的行为(接受但不强制)听起来更像MySQL。 – beldaz 2015-03-06 21:45:54

回答

4

由于@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。在这种情况下,AFTER优于BEFORE。如果任何其他条件可以取消中间的DML语句(与其他触发器一样),则性能会更好。

如果你什么也没有,那么BEFORE触发器可能会更好。在这种情况下,请务必使触发功能RETURN NEW/OLD相应。

2

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

解决方法是使用trigger代替;您可以将其设置为在任何插入ORDERS之前运行,并在检测到此问题时引发错误。 (我假设更新ORDERS绝不会介绍这个问题,但如果他们能,那么你就需要对这种情况下的触发也。)

+0

真的吗?因为这就是我们被教导做出断言的方式,而且我们正在使用postgreSQL。 – gestalt 2012-03-11 17:56:34

+0

@gestalt:真的。 PostgreSQL可以支持比任何其他主要数据库管理系统更多的SQL标准 - 并且更接近它支持的标准 - 所以它对你的班级使用它是合乎逻辑的。但这不是100%的方式。 (这并不意味着您不应该了解它不支持的标准方面,只是意味着您会学到一些无法直接测试的东西。) – ruakh 2012-03-11 19:11:52

+0

Sybase SQL Anywhere支持CREATE ASSERTION'并执行它们。 – onedaywhen 2012-03-12 09:55:21