2012-03-11 3 views
1

J'essaye de créer une base de données simple où j'ai une table des données de client et une table des données de commande. J'essaie d'écrire une contrainte qui fait qu'un client ne peut pas commander plus d'une quantité spécifique d'articles un jour donné. Voici ce que j'ai:Contraintes et assertions dans 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 
); 

Et c'est ce que j'ai écrit pour appliquer cette contrainte, mais cela ne fonctionne pas. Je suppose que c'est parce que ORDER_NUM et ORDER_DATE n'ont jamais des valeurs égales.

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 

Ma question est comment faire fonctionner cette contrainte, comme comment limiter le nombre de commandes par jour.

+0

L'assertion n'est pas un [travail clé réservé pour postgresql] (http://www.postgresql.org/docs/9.1/interactive/sql-keywords-appendix.html), bien que ce soit pour sql-99 et 92. –

+1

@Clodoaldo: Ce n'est pas pertinent. C'est un mot clé dans PostgreSQL, ce n'est pas un mot clé * réservé *; ce qui signifie que, bien que 'ASSERTION' soit compris comme un mot clé dans un contexte comme' CREATE ASSERTION ... ', il est autorisé à être utilisé comme identifiant (donc quelque chose comme' CREATE TABLE assertion ... 'est autorisé). Et PostgreSQL n'accepte * pas * les instructions CREATE ASSERTION, mais il n'applique pas les assertions qu'elles décrivent. – ruakh

+0

@ruakh Si vous essayez de créer une assertion dans PostgreSQL (au moins jusqu'à 9.4), vous obtiendrez ERROR: CREATE ASSERTION n'est pas encore implémenté. Donc je dirais que ça ne les accepte pas. Le comportement que vous décrivez (accepter, mais ne pas appliquer) ressemble plus à MySQL. – beldaz

Répondre

4

Comme @ruakh déjà éclairci, il n'y a pas CREATE ASSERTION dans PostgreSQL . Vérifiez simplement le list of SQL commands. Ce n'est pas là.

Vous pouvez utiliser des déclencheurs qui mettent à jour un nombre par client combiné avec une contrainte CHECK, mais vous devez couvrir toutes les instructions DML pertinentes: INSERT, UPDATE, DELETE. Pourrait ressembler à ceci:

Préparer table des clients existants:

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); 

créer des fonctions de déclenchement et déclenche:

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(); 

J'ai fait tous ces déclencheurs déclencheurs AFTER - c'est la raison pour laquelle il est autorisé à RETURN NULL. APRÈS est préférable à AVANT dans ce cas. Il fonctionne mieux si d'autres conditions pouvaient annuler les instructions DML au milieu (comme les autres déclencheurs).

Si vous n'avez rien de tel, alors les déclencheurs AVANT peuvent être préférables. Veillez à ce que les fonctions de déclenchement RETURN NEW/OLD correspondent à ce cas.

2

Je ne crois pas que PostgreSQL applique les instructions CREATE ASSERTION; Au moins, "Assertions" est répertorié comme une fonctionnalité non prise en charge dans Appendix D.2 of the PostgreSQL Manual. Pour autant que je sache, en réalité, aucun des principaux SGBD les applique.

La solution consiste à utiliser un trigger à la place; vous pouvez le configurer pour qu'il s'exécute avant les insertions sur ORDERS et pour déclencher une erreur s'il détecte ce problème. (Je suppose que les mises à jour sur ORDERS ne seront jamais introduire ce problème, mais s'ils le peuvent, alors vous auriez besoin d'un déclencheur pour ce cas aussi.)

+0

Vraiment? Parce que c'est ainsi que nous avons appris à faire des assertions, et nous utilisons postgreSQL. – gestalt

+0

@gestalt: Vraiment. PostgreSQL supporte sans doute plus de standard SQL que tout autre SGBD majeur - et se rapproche davantage de la norme dans ce qu'il * supporte * - il est donc logique que votre classe l'utilise. Mais ce n'est pas à 100% du chemin. (Cela ne signifie pas que vous ne devriez pas connaître les aspects de la norme qui ne sont pas pris en charge, mais simplement que vous apprendrez des choses que vous ne pouvez pas tester directement.) – ruakh

+0

Sybase SQL Anywhere prend en charge 'CREATE ASSERTION' et les applique. – onedaywhen