【问题标题】:Using date in a check constraint, Oracle在检查约束中使用日期,Oracle
【发布时间】:2011-03-16 22:22:17
【问题描述】:

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

【问题讨论】:

    标签: oracle date constraints


    【解决方案1】:

    不幸的是,检查约束不能引用像 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;
    

    【讨论】:

    【解决方案2】:

    您不能在检查约束中使用 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

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

    无论如何:我不确切知道您要达到什么目的,但我认为您可以为此目的使用 触发器

    【讨论】:

      【解决方案3】:

      每次更新记录时,SYSDATE 都会有不同的值。因此,约束每次都会以不同的方式验证。由于这个原因,Oracle 不允许在约束中使用 sysdate。

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

      还有:(StartDate &gt; (CloseDate + (SYSDATE + 730)))) 是什么?您不能添加日期。

      并且:StartDate 需要 CloseDate 之后?这不奇怪吗?

      【讨论】:

        【解决方案4】:

        将 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
        

        【讨论】:

          【解决方案5】:

          你可以通过这样的小作弊来实现这一点:

          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 会变得更加智能,并且不会再允许这样的伎俩了。

          【讨论】:

            【解决方案6】:

            我不建议将触发器用作约束并引发异常,相反,您可以使用一列将 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))));
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-11-29
              • 2021-03-27
              • 1970-01-01
              相关资源
              最近更新 更多