2011-03-16 5 views
5

J'essaie de vérifier l'ajout de la contrainte suivante, mais Oracle renvoie l'erreur ci-dessous.Utilisation de la date dans une contrainte de vérification, Oracle

ALTER TABLE Table1 
ADD (CONSTRAINT GT_Table1_CloseDate 
CHECK (CloseDate > SYSDATE), 
CONSTRAINT LT_Table1_CloseDate 
CHECK (CloseDate <= SYSDATE + 365)), 
CONSTRAINT GT_Table1_StartDate 
CHECK (StartDate > (CloseDate + (SYSDATE + 730)))); 

Erreur:

Error report: 
SQL Error: ORA-02436: date or system variable wrongly specified in CHECK constraint 
02436. 00000 - "date or system variable wrongly specified in CHECK constraint" 
*Cause: An attempt was made to use a date constant or system variable, 
      such as USER, in a check constraint that was not completely 
      specified in a CREATE TABLE or ALTER TABLE statement. For 
      example, a date was specified without the century. 
*Action: Completely specify the date constant or system variable. 
      Setting the event 10149 allows constraints like "a1 > '10-MAY-96'", 
      which a bug permitted to be created before version 8. 

Répondre

11

Une contrainte de vérification, malheureusement, ne peut pas faire référence à une fonction comme SYSDATE. Vous devez créer un déclencheur qui vérifie ces valeurs lorsque DML se produit, à savoir

CREATE OR REPLACE TRIGGER trg_check_dates 
    BEFORE INSERT OR UPDATE ON table1 
    FOR EACH ROW 
BEGIN 
    IF(:new.CloseDate <= SYSDATE) 
    THEN 
    RAISE_APPLICATION_ERROR(-20001, 
      'Invalid CloseDate: CloseDate must be greater than the current date - value = ' || 
      to_char(:new.CloseDate, 'YYYY-MM-DD HH24:MI:SS')); 
    END IF; 
    IF(:new.CloseDate > add_months(SYSDATE,12)) 
    THEN 
    RAISE_APPLICATION_ERROR(-20002, 
     'Invalid CloseDate: CloseDate must be within the next year - value = ' || 
     to_char(:new.CloseDate, 'YYYY-MM-DD HH24:MI:SS')); 
    END IF; 
    IF(:new.StartDate <= add_months(:new.CloseDate,24)) 
    THEN 
    RAISE_APPLICATION_ERROR(-20002, 
      'Invalid StartDate: StartDate must be within 24 months of the CloseDate - StartDate = ' || 
      to_char(:new.StartDate, 'YYYY-MM-DD HH24:MI:SS') || 
      ' CloseDate = ' || to_char(:new.CloseDate , 'YYYY-MM-DD HH24:MI:SS')); 
    END IF; 
END; 
+1

Notez que pour la deuxième « contrainte » LT_Table1_CloseDate, vous pouvez utiliser une contrainte de vérification dans 11g en utilisant des colonnes virtuelles (http://rwijk.blogspot.com/2007/12/check- constraints-with-sysdate.html) –

4

Chaque fois que l'enregistrement est mis à jour SYSDATE aura une valeur différente. Par conséquent, la contrainte sera validée différemment à chaque fois. Oracle n'autorise pas sysdate dans une contrainte pour cette raison.

Vous pouvez résoudre votre problème avec un déclencheur qui vérifie si CloseDate a effectivement changé et déclenche une exception lorsque la nouvelle valeur n'est pas dans la plage.

Et: Qu'est-ce que (StartDate > (CloseDate + (SYSDATE + 730))))? Vous ne pouvez pas ajouter de dates.

Et: StartDate doit être aprèsCloseDate? Est-ce que ce n'est pas bizarre?

11

Vous ne pouvez pas utiliser SYSDATE dans la contrainte de vérification. Selon la documentation

Conditions of check constraints cannot contain the following constructs:

  • Subqueries and scalar subquery expressions
  • Calls to the functions that are not deterministic (CURRENT_DATE,
    CURRENT_TIMESTAMP, DBTIMEZONE,
    LOCALTIMESTAMP, SESSIONTIMEZONE,
    SYSDATE, SYSTIMESTAMP, UID, USER, and USERENV)
  • Calls to user-defined functions
  • Dereferencing of REF columns (for example, using the DEREF function)
  • Nested table columns or attributes
  • The pseudocolumns CURRVAL, NEXTVAL, LEVEL, or ROWNUM
  • Date constants that are not fully specified

Pour 10g Release 2 (10.2), voir constraint, et 11g Release 2 (11.2) voir constraint. Rappelez-vous qu'une contrainte d'intégrité est une instruction sur les données de table toujours true.

Quoi qu'il en soit: Je ne sais pas exactement ce que vous essayez d'atteindre, mais je pense que vous pouvez utiliser déclencheurs à cet effet.

3

Ecrivez sysdate dans une colonne et utilisez-le pour la validation. Cette colonne pourrait être votre colonne d'audit (Pour exemple: date de création)

CREATE TABLE "AB_EMPLOYEE22" 
(
    "NAME"  VARCHAR2 (20 BYTE), 
    "AGE"  NUMBER, 
    "SALARY" NUMBER, 
    "DOB"  DATE, 
    "DOJ"  DATE DEFAULT SYSDATE 
); 

Table Created  

ALTER TABLE "AB_EMPLOYEE22" ADD CONSTRAINT 
AGE_CHECK CHECK((ROUND((DOJ-DOB)/365)) = AGE) ENABLE; 

Table Altered 
0

Vous pouvez y parvenir lorsque vous faites un peu de triche comme ceci:

CREATE OR REPLACE FUNCTION SYSDATE_DETERMINISTIC RETURN DATE DETERMINISTIC IS 
BEGIN 
    RETURN SYSDATE; 
END SYSDATE_DETERMINISTIC; 
/

CREATE TABLE Table1 (
    s_date DATE, 
    C_DATE DATE GENERATED ALWAYS AS (SYSDATE_DETERMINISTIC()) 
); 

ALTER TABLE Table1 ADD CONSTRAINT s_check CHECK (s_date < C_DATE); 

Bien sûr, la fonction SYSDATE_DETERMINISTIC est pas déterministe mais Oracle permet de le déclarer quand même. Dans les prochaines versions, Oracle deviendra peut-être plus intelligent et ne permettra plus de telles astuces.

0

Je recommande don`t chanter les déclencheurs comme une contrainte et de soulever des exceptions plutôt que vous pouvez utiliser une colonne pour stocker SYSDATE comme date de registre (si vous l'avez déjà, vous pouvez l'utiliser), puis votre contrainte compare cette colonne au lieu de SYSDATE

ALTER TABLE Table1 
ADD (REGISTER_DATE DATE); 

CREATE OR REPLACE TRIGGER trg_check_dates 
    BEFORE INSERT OR UPDATE ON table1 
    FOR EACH ROW 
BEGIN 
    :new.REGISTER_DATE := SYSDATE; 
END; 

ALTER TABLE Table1 
ADD (CONSTRAINT GT_Table1_CloseDate 
CHECK (CloseDate > REGISTER_DATE), 
CONSTRAINT LT_Table1_CloseDate 
CHECK (CloseDate <= REGISTER_DATE + 365)), 
CONSTRAINT GT_Table1_StartDate 
CHECK (StartDate > (CloseDate + (REGISTER_DATE + 730)))); 
Questions connexes