【问题标题】:oracle constraints datatypeoracle 约束数据类型
【发布时间】:2020-07-24 19:02:14
【问题描述】:

我创建了一个数据类型为 smallint 的表。

create table test(
     A smallint, 
     constraints ACHECK check(A between 1 and 5));

我想添加只允许用户添加 1~5 之间的整数值范围的约束。 但是,即使有约束,我仍然能够插入自动四舍五入的浮点值。

insert into test values(3.2);

如何使此代码显示错误? 我不允许更改数据类型。

【问题讨论】:

  • 为什么不允许更改数据类型?这是课堂作业吗?
  • "自动向上取整的浮点值" 实际上它被四舍五入到最接近的整数,向上或向下。
  • smallint 我假设你的意思是integer,integer 我当然是指number(38)。

标签: sql oracle floating-point int constraints


【解决方案1】:

你不能轻易地做你想做的事。 Oracle 正在将输入值3.2 转换为整数。整数满足约束。值3 是插入的内容。转换发生在幕后。 Oracle 的开发人员认为这种转换是一件“好事”。

您可以通过将列声明为 number 然后检查它是否为整数来解决此问题:

create table test (
     A number, 
     constraints ACHECK check(A between 1 and 5 and mod(A, 1) = 0)
);

【讨论】:

  • 很遗憾,“我不允许更改数据类型。”
  • 我相信 Gordon Linoff 的解决方案应该适合您。无论在何处使用此字段,由于他列出的检查,该数字应与 smallint 兼容。
  • @Littlefoot 。 . .那么您就无法进行此检查 - 至少不容易。 Oracle 会自动进行转换,在这种情况下不会返回错误。
  • 我知道,戈登。我自己试过了。触发器也无济于事,转换是在触发器触发之前完成的。
  • 我发布了一个解决问题的方法,虽然很复杂。
【解决方案2】:

就要求不更改数据类型而言,但它并没有说明有关创建新对象的任何内容,我想出了一个非常复杂的解决方案,它可以解决问题,但我会到目前为止,我更喜欢将数据类型更改为数字并使用普通约束。

这里的主要问题是值的向上取整是在解析语句之后但在执行之前完成的。作为一种内部机制,您对此无能为力。如果您使用触发器并在插入或更新列之前显示 :NEW 的值,您可以很容易地看到这种情况。

但是,有一个技巧。 F.G.A.在解析之前将原始值传递给语句。因此,使用带有处理程序和两个触发器的策略就可以了。

让我详细说明

SQL> create table testx ( xsmall smallint );

Table created.

SQL> create table tracex ( id_timestamp timestamp , who_was varchar2(50) , sqltext varchar2(4000) );

Table created.

SQL> create or replace procedure pr_handle_it (object_schema VARCHAR2, object_name VARCHAR2, policy_name VARCHAR2) 
is
  begin
    -- dbms_output.put_line('SQL was: ' || SYS_CONTEXT('userenv','CURRENT_SQL'));
    insert into tracex values ( systimestamp , sys_context('userenv','session_user') , sys_context('userenv','current_sql') );
    commit;
end;
/

Procedure created.

SQL> BEGIN
  DBMS_FGA.ADD_POLICY(
    object_schema => 'MYSCHEMA',
      object_name => 'TESTX',
      policy_name => 'MY_NEW_POLICY',
      audit_condition => null,
      audit_column => 'XSMALL',
      handler_schema => 'MYSCHEMA',
      handler_module => 'PR_HANDLE_IT',
      enable => true,
      statement_types => 'INSERT, UPDATE, DELETE'
    );
END;
/

PL/SQL procedure successfully completed.

SQL> create or replace trigger trg_testx before insert or update on testx 
referencing new as new old as old
for each row
begin
if inserting or updating
then
    dbms_output.put('  New value is: ' || :new.xsmall);
    dbms_output.put_line('TRIGGER : The value for CURRENT_SQL is '||sys_context('userenv','current_sql'));
    insert into tracex values ( systimestamp , sys_context('userenv','session_user') , sys_context('userenv','current_sql') );
end if;
end;
/

Trigger created.

SQL> create or replace trigger trg_testx2 after insert or update on cpl_rep.testx 
referencing new as new old as old
for each row
declare
v_val pls_integer;
begin
    if inserting or updating 
    then
        select regexp_replace(sqltext,'[^0-9]+','') into v_val 
        from ( select upper(sqltext) as sqltext from tracex order by id_timestamp desc ) where rownum = 1 ;
        if v_val > 5 
        then 
            raise_application_error(-20001,'Number greater than 5 or contains decimals');
        end if;
    end if;
end ;
/ 

Trigger created.

这些是元素:

-解析前获取查询的一张跟踪表

-一个关于更新和插入的 FGA 策略

-处理程序的一个过程

-两个触发器之前(获取查询和原始值)和之后一个触发器来评估语句中的值而不是四舍五入的值。

由于触发器是按顺序计算的,before 插入带有小数的原始值并在向上舍入之前执行,after 分析存储在跟踪表中的值以引发异常。

SQL> insert into testx values ( 1 ) ;

1 row created.

SQL>  insert into testx values ( 5 ) ;

1 row created.

SQL>  insert into testx values ( 2.1 ) ;
 insert into testx values ( 2.1 )
             *
ERROR at line 1:
ORA-20001: Number greater than 5 or it contains decimals
ORA-06512: at "CPL_REP.TRG_TESTX2", line 10
ORA-04088: error during execution of trigger 'CPL_REP.TRG_TESTX2'

SQL> insert into testx values ( 6 ) ;
insert into testx values ( 6 )
            *
ERROR at line 1:
ORA-20001: Number greater than 5 or it contains decimals
ORA-06512: at "CPL_REP.TRG_TESTX2", line 10
ORA-04088: error during execution of trigger 'CPL_REP.TRG_TESTX2'

总结:正如@Gordon Linoff 所说,没有简单的方法可以实现所要求的。我相信该方法对于要求来说非常复杂。我只是想出它的目的是为了表明它毕竟是可能的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-15
    • 1970-01-01
    • 2012-09-28
    • 1970-01-01
    • 2021-11-06
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    相关资源
    最近更新 更多