在检查约束中使用date,Oracle
我想检查添加以下约束,但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.
不幸的是,检查约束不能引用像SYSDATE这样的函数。 您将需要创build一个触发器来检查这些值,当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;
在检查约束中不能使用SYSDATE。 根据文件
检查约束条件不能包含以下结构:
- 子查询和标量子查询expression式
- 调用不确定性函数(CURRENT_DATE,
CURRENT_TIMESTAMP,DBTIMEZONE,
LOCALTIMESTAMP,SESSIONTIMEZONE,
SYSDATE ,SYSTIMESTAMP,UID,USER和USERENV)- 调用用户定义的函数
- REF列的解引用(例如,使用DEREF函数)
- 嵌套表格列或属性
- 伪列CURRVAL,NEXTVAL,LEVEL或ROWNUM
- date常量没有完全指定
对于10g第2版(10.2) ,见约束,对于11g第2版(11.2),见约束 。
请记住,一个完整性约束是关于表数据的一个总是为真的语句。
无论如何:我不知道你到底想做什么,但我想你可以使用触发器来实现这个目的。
每次logging更新时,SYSDATE将具有不同的值。 因此,约束每次都会有不同的validation。 由于这个原因,Oracle不允许sysdate作为约束。
你可以通过一个触发器来解决你的问题,该触发器检查CloseDate是否真的发生了变化,并在新值不在范围内时引发exception。
并且:什么是(StartDate > (CloseDate + (SYSDATE + 730))))
? 你不能添加date。
并且: StartDate
需要在 CloseDate
之后 ? 这不奇怪吗?
将sysdate写入列并将其用于validation。 这一列可能是您的审计专栏(例如:创builddate)
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
你可以做到这一点,当你这样做一个小骗子:
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变得更加智能,不再允许这样的技巧。
我不build议使用触发器作为约束,并引发exception,而是可以使用列来存储SYSDATE作为注册date(如果您已经拥有它,那么您可以使用它),然后您的约束将比较此列而不是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))));