就要求不更改数据类型而言,但它并没有说明有关创建新对象的任何内容,我想出了一个非常复杂的解决方案,它可以解决问题,但我会到目前为止,我更喜欢将数据类型更改为数字并使用普通约束。
这里的主要问题是值的向上取整是在解析语句之后但在执行之前完成的。作为一种内部机制,您对此无能为力。如果您使用触发器并在插入或更新列之前显示 :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 所说,没有简单的方法可以实现所要求的。我相信该方法对于要求来说非常复杂。我只是想出它的目的是为了表明它毕竟是可能的。