2012-03-11 248 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