0

你如何编写一个程序来显示一个字段的值不能高于另一个字段的值,就数字而言。说。雇员的薪水不能高于其经理的薪水。我以前从来没有做过

4

1 回答 1

2

在 SQL 中没有声明式的方式来执行这样的业务规则。所以必须用代码来完成。有许多陷阱,其中最重要的是确定需要强制执行规则的所有场景。

以下是场景:

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

以下是使这一切变得更加困难的事情:

  1. 执行这些规则涉及从我们正在操作的表中进行选择,因此由于 ORA-04088: mutating tables 异常,我们不能使用 BEFORE ... FOR EACH ROW 触发器。
  2. 此外,从表中选择意味着我们不能在多用户模式下运行,因为读取一致性(否则会话 #1 可能会继续对员工加薪,而忽略会话 #2 当前正在应用减薪的事实)该员工的经理)。

因此,出于所有这些原因,执行此类业务规则的唯一方法是使用 API。构建一个存储过程,永远不要让任何进程对表进行裸 DML 访问。

以下代码块仅在更新员工工资时强制执行该规则。兴趣点包括:

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

    创建或替换过程 change_emp_sal(emp.empno%type 中的 p_eno,emp.sal%type 中的 p_new_sal)是类型 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); begin -- 锁定员工记录 select * into l_emp from emp where empno = p_eno for update of 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 ('错误!下属赚取'||l_subords(l_idx).sal); 回滚;增加; 结束change_emp_sal;/

以下是 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>

所以它有效,就它而言。它只处理五个场景中的两个。重构代码以满足其他三个要求留给读者作为练习。

于 2010-05-02T13:29:36.527 回答