在检查约束中使用日期,Oracle

时间:2011-03-16 22:22:18

标签: oracle date constraints

我正在尝试检查添加以下约束,但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))));

错误:

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.

6 个答案:

答案 0 :(得分:16)

遗憾的是,检查约束无法引用像SYSDATE这样的函数。您需要创建一个触发器,在DML发生时检查这些值,即

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 :(得分:14)

您不能在检查约束中使用SYSDATE。根据文件

  

检查条件的条件不能   包含以下构造:

     
      
  • 子查询和标量子查询表达式
  •   
  • 调用不确定的函数(CURRENT_DATE,
      CURRENT_TIMESTAMP,DBTIMEZONE,
      LOCALTIMESTAMP,SESSIONTIMEZONE,
       SYSDATE ,SYSTIMESTAMP,UID,USER和   USERENV)
  •   
  • 调用用户定义的函数
  •   
  • 取消引用REF列(例如,使用DEREF函数)
  •   
  • 嵌套表格列或属性
  •   
  • 伪列CURRVAL,NEXTVAL,LEVEL或ROWNUM
  •   
  • 未完全指定的日期常量
  •   

对于 10g第2版(10.2),请参阅constraint, 11g第2版(11.2),请参阅constraint

请记住,完整性约束是关于始终为真的表数据的声明。

无论如何:我不确切地知道你想要达到的目的,但我认为你可以使用触发器来达到这个目的。

答案 2 :(得分:5)

将sysdate写入列并将其用于验证。此列可能是您的审核列(例如:创建日期)

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

答案 3 :(得分:4)

每次更新记录时,SYSDATE都会有不同的值。因此,约束每次都会有不同的验证。由于这个原因,Oracle不允许sysdate进入约束。

您可以使用触发器来解决问题,该触发器检查CloseDate是否实际发生了更改,并在新值不在范围内时引发异常。

而且:(StartDate > (CloseDate + (SYSDATE + 730))))是什么?你不能添加日期。

而且:StartDate需要在 CloseDate之后?那不奇怪吗?

答案 4 :(得分:1)

当你像这样做一点作弊时,你可以做到这一点:

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

当然,函数SYSDATE_DETERMINISTIC 是确定性的,但Oracle仍允许声明这一点。

也许在未来的版本中,Oracle变得更加智能化,不再允许这样的技巧。

答案 5 :(得分:0)

我不推荐使用单个触发器作为约束并引发异常,而是可以使用一列来存储 SYSDATE 作为注册日期(如果您已经拥有它,那么您可以使用它)然后您的约束比较此列而不是 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))));