使用Oracle中的过程实施业务规则

时间:2010-04-27 01:18:13

标签: oracle stored-procedures business-rules

如何编写一个程序,表明一个字段的值不能高于另一个字段的值,就数字而言。说。员工的工资不能高于经理的工资。

之前我从未做过一次

1 个答案:

答案 0 :(得分:2)

在SQL中没有以声明方式强制执行此类业务规则。所以必须用代码完成。有许多问题,其中最重要的是确定需要强制执行规则的所有方案

以下是方案:

  1. 当我们插入一名员工时,我们需要检查他们的薪水是否高于其经理薪水的90%。
  2. 当我们更新员工的工资时,我们需要检查它是否仍然不超过其经理薪水的90%。
  3. 当我们更新经理的薪水时,我们需要检查它是否仍然超过所有他们下属工资的110%。
  4. 如果我们同时为经理及其下属插入记录(比如使用INSERT ALL),我们需要确保该规则仍然有效。
  5. 如果我们将员工从一个经理移到另一个经理,我们需要确保该规则仍然有效。
  6. 以下是使这一切变得更难的事情:

    1. 执行这些规则涉及从我们正在操作的表中进行选择,因此我们不能使用BEFORE ... FOR EACH ROW触发器,因为ORA-04088:变异表异常。
    2. 此外,从表中进行选择意味着我们无法在多用户模式下运行,因为读取的一致性(否则会话#1可以继续向员工支付加薪,而不会因为会话#2当前正在应用支付减少给该员工的经理。)
    3. 因此,出于所有这些原因,执行此类业务规则的唯一方法是使用API​​;构建一个存储过程,永远不要让任何进程对表进行裸DML访问。

      以下chunk o'代码在更新员工薪水时强制执行规则。兴趣点包括:

      • 它具有用户定义的例外以识别规则违规。实际上这些应该在包规范中定义,因此其他程序单元可以引用它们。
      • 使用SELECT ... FOR UPDATE来锁定感兴趣的行。
      • 使用COMMIT和ROLLBACK来释放锁。在实际的实现中,这可能会以不同的方式处理(即通过调用程序)。

        创建或替换过程change_emp_sal     (p.eeno在emp.empno%类型中       ,emp.sal%type中的p_new_sal) 是     type emp_nt是emp%rowtype的表;     l_emp emp%rowtype;     l_mgr emp%rowtype;     l_subords emp_nt;     l_idx pls_integer;     x_mgr_not_paid_enough例外;     pragma exception_init(x_mgr_not_paid_enough,-20000);     x_sub_paid_too_much例外;     pragma exception_init(x_sub_paid_too_much,-20001); 开始      - 锁定员工记录     select * into l_emp     来自emp     其中empno = p_eno     更新sal;

        -- lock their manager's record (if they have one)
        if l_emp.mgr is not null
        then
            select * into l_mgr
            from emp
            where empno = l_emp.mgr
            for update;
        end if;
        
        -- lock their subordinates' records
        select * bulk collect into l_subords
        from emp
        where mgr = p_eno
        for update;
        
        -- compare against manager's salary
        if l_mgr.sal is not null
           and l_mgr.sal < ( p_new_sal * 1.1 )
        then
            raise x_mgr_not_paid_enough;
        end if;
        
        -- compare against subordinates' salaries
        for i in 1..l_subords.count()
        loop
            if l_subords(i).sal > ( p_new_sal * 0.9 )
            then
                l_idx := i;
                raise x_sub_paid_too_much;
            end if;
        end loop;
        
        -- no exceptions raised so we can go ahead
        update emp
        set    sal = p_new_sal
        where empno = p_eno;
        
        --  commit to free the locks
        commit;
        

        例外     当x_mgr_not_paid_enough然后         dbms_output.put_line('错误!管理器只赚取'|| l_mgr.sal);         回滚;         提高;     当x_sub_paid_too_much然后         dbms_output.put_line('Error!subordinate earns'|| l_subords(l_idx).sal);         回滚;         提高; 结束change_emp_sal; /

      以下是Deptarment 50的四名员工:

      SQL> select e.empno, e.ename, e.sal, m.ename as mgr_name, m.empno as mgr_no
        2  from emp e join emp m on (e.mgr = m.empno)
        3  where e.deptno = 50
        4  order by sal asc
        5  /
      
           EMPNO ENAME             SAL MGR_NAME       MGR_NO
      ---------- ---------- ---------- ---------- ----------
            8060 VERREYNNE        2850 FEUERSTEIN       8061
            8085 TRICHLER         3500 FEUERSTEIN       8061
            8100 PODER            3750 FEUERSTEIN       8061
            8061 FEUERSTEIN       4750 SCHNEIDER        7839
      
      SQL>
      

      让我们试着给比利一个大加注,这应该会失败......

      SQL> exec change_emp_sal (8060, 4500)
      Error! manager only earns 4750
      BEGIN change_emp_sal (8060, 4500); END;
      
      *
      ERROR at line 1:
      ORA-20000:
      ORA-06512: at "APC.CHANGE_EMP_SAL", line 67
      ORA-06512: at line 1
      
      
      SQL>
      

      好的,让我们给比利一个较小的加注,这应该成功......

      SQL> exec change_emp_sal (8060, 4000)
      
      PL/SQL procedure successfully completed.
      
      SQL>
      

      现在让我们试着让史蒂文大幅减薪,这应该会失败......

      SQL> exec change_emp_sal (8061, 3500)
      Error! subordinate earns 3500
      BEGIN change_emp_sal (8061, 3500); END;
      
      *
      ERROR at line 1:
      ORA-20001:
      ORA-06512: at "APC.CHANGE_EMP_SAL", line 71
      ORA-06512: at line 1
      
      
      SQL>
      

      所以,让我们给史蒂文一个象征性的减薪,这应该成功......

      SQL> exec change_emp_sal (8061, 4500)
      
      PL/SQL procedure successfully completed.
      
      SQL>
      

      这是新的薪酬结构......

      SQL> select e.empno, e.ename, e.sal, m.ename as mgr_name, m.empno as mgr_no
        2  from emp e join emp m on (e.mgr = m.empno)
        3  where e.deptno = 50
        4  order by sal asc
        5  /
      
           EMPNO ENAME             SAL MGR_NAME       MGR_NO
      ---------- ---------- ---------- ---------- ----------
            8085 TRICHLER         3500 FEUERSTEIN       8061
            8100 PODER            3750 FEUERSTEIN       8061
            8060 VERREYNNE        4000 FEUERSTEIN       8061
            8061 FEUERSTEIN       4500 SCHNEIDER        7839
      
      SQL>
      

      所以它的确有效。它只处理五个场景中的两个。重构代码以满足其他三个代码留给读者练习。