Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 在Oracle数据库中添加约束_Sql_Oracle_Plsql - Fatal编程技术网

Sql 在Oracle数据库中添加约束

Sql 在Oracle数据库中添加约束,sql,oracle,plsql,Sql,Oracle,Plsql,我们希望在Oracle数据库中添加一个约束,以检查员工的工资是否符合层次结构。 员工有身份证、姓名、职级和工资。 我们有三个等级rank1,rank2,rank3,rank1优于rank2,rank2优于rank3。 添加rank2的员工时,其工资不应高于rank1员工的工资 您能告诉我们哪种解决方案是实现此约束的最佳方案吗 非常感谢这不能通过检查约束来完成,因为它们只能查看正在插入或更新的行中的值,而不能查看其他表或同一表的其他行 我尝试了一种使用物化视图和检查约束的方法-请参阅。诀窍是创建规

我们希望在Oracle数据库中添加一个约束,以检查员工的工资是否符合层次结构。 员工有身份证、姓名、职级和工资。 我们有三个等级rank1,rank2,rank3,rank1优于rank2,rank2优于rank3。 添加rank2的员工时,其工资不应高于rank1员工的工资

您能告诉我们哪种解决方案是实现此约束的最佳方案吗


非常感谢

这不能通过检查约束来完成,因为它们只能查看正在插入或更新的行中的值,而不能查看其他表或同一表的其他行

我尝试了一种使用物化视图和检查约束的方法-请参阅。诀窍是创建规则异常的物化视图-即,物化视图应始终为空。然后,向始终失败的MV添加检查约束,例如检查1=0

对于您的情况,解决方案如下所示:

create materialized view emp_emp_mv
refresh complete on commit as
select 1 dummy
from emp e1, emp e2
where e1.empno != e2.empno
and e1.rank < e2.rank
and e1.sal > e2.sal;

alter table emp_emp_mv
add constraint emp_emp_mv_chk
check (1=0) deferrable;
CREATE OR REPLACE TRIGGER EMPLOYEES_AIU
  AFTER INSERT OR UPDATE ON EMPLOYEES
  -- Note: There is no FOR EACH ROW here so 'OLD' and 'NEW' values are not available
DECLARE
  strPrev_rank      EMPLOYEES.RANK%TYPE;
  nPrev_max_salary  EMPLOYEES.SALARY%TYPE;
BEGIN
  FOR aRow IN (SELECT RANK, MAX(SALARY) AS MAX_SALARY
                 FROM EMPLOYEES
                 GROUP BY RANK
                 ORDER BY RANK DESC)
  LOOP
    IF nPrev_max_salary IS NOT NULL AND
         aRow.MAX_SALARY > nPrev_max_salary THEN
      RAISE_APPLICATION_ERROR(-20101, 'Max salary (' || aRow.MAX_SALARY||
                                      ') of rank ' || aRow.RANK ||
                                      ' exceeds max salary (' || nPrev_max_salary ||
                                      ') of rank ' || strPrev_rank);
    END IF;

    strPrev_rank := aRow.RANK;
    nPrev_max_salary := aRow.MAX_SALARY;
  END LOOP;
END EMPLOYEES_AIU;
请注意:

我没有测试上面的例子 我从未在实际系统中使用过这种方法,只是在实验中。
不能使用check约束进行检查,因为这些约束只能查看正在插入或更新的行中的值,而不能查看同一表的其他表或其他行中的值

我尝试了一种使用物化视图和检查约束的方法-请参阅。诀窍是创建规则异常的物化视图-即,物化视图应始终为空。然后,向始终失败的MV添加检查约束,例如检查1=0

对于您的情况,解决方案如下所示:

create materialized view emp_emp_mv
refresh complete on commit as
select 1 dummy
from emp e1, emp e2
where e1.empno != e2.empno
and e1.rank < e2.rank
and e1.sal > e2.sal;

alter table emp_emp_mv
add constraint emp_emp_mv_chk
check (1=0) deferrable;
CREATE OR REPLACE TRIGGER EMPLOYEES_AIU
  AFTER INSERT OR UPDATE ON EMPLOYEES
  -- Note: There is no FOR EACH ROW here so 'OLD' and 'NEW' values are not available
DECLARE
  strPrev_rank      EMPLOYEES.RANK%TYPE;
  nPrev_max_salary  EMPLOYEES.SALARY%TYPE;
BEGIN
  FOR aRow IN (SELECT RANK, MAX(SALARY) AS MAX_SALARY
                 FROM EMPLOYEES
                 GROUP BY RANK
                 ORDER BY RANK DESC)
  LOOP
    IF nPrev_max_salary IS NOT NULL AND
         aRow.MAX_SALARY > nPrev_max_salary THEN
      RAISE_APPLICATION_ERROR(-20101, 'Max salary (' || aRow.MAX_SALARY||
                                      ') of rank ' || aRow.RANK ||
                                      ' exceeds max salary (' || nPrev_max_salary ||
                                      ') of rank ' || strPrev_rank);
    END IF;

    strPrev_rank := aRow.RANK;
    nPrev_max_salary := aRow.MAX_SALARY;
  END LOOP;
END EMPLOYEES_AIU;
请注意:

我没有测试上面的例子 我从未在实际系统中使用过这种方法,只是在实验中。
我认为您最终将不得不使用触发器,因为您需要引用触发器所在的同一个表中的数据,所以它需要是表触发器,而不是行触发器。大概是这样的:

create materialized view emp_emp_mv
refresh complete on commit as
select 1 dummy
from emp e1, emp e2
where e1.empno != e2.empno
and e1.rank < e2.rank
and e1.sal > e2.sal;

alter table emp_emp_mv
add constraint emp_emp_mv_chk
check (1=0) deferrable;
CREATE OR REPLACE TRIGGER EMPLOYEES_AIU
  AFTER INSERT OR UPDATE ON EMPLOYEES
  -- Note: There is no FOR EACH ROW here so 'OLD' and 'NEW' values are not available
DECLARE
  strPrev_rank      EMPLOYEES.RANK%TYPE;
  nPrev_max_salary  EMPLOYEES.SALARY%TYPE;
BEGIN
  FOR aRow IN (SELECT RANK, MAX(SALARY) AS MAX_SALARY
                 FROM EMPLOYEES
                 GROUP BY RANK
                 ORDER BY RANK DESC)
  LOOP
    IF nPrev_max_salary IS NOT NULL AND
         aRow.MAX_SALARY > nPrev_max_salary THEN
      RAISE_APPLICATION_ERROR(-20101, 'Max salary (' || aRow.MAX_SALARY||
                                      ') of rank ' || aRow.RANK ||
                                      ' exceeds max salary (' || nPrev_max_salary ||
                                      ') of rank ' || strPrev_rank);
    END IF;

    strPrev_rank := aRow.RANK;
    nPrev_max_salary := aRow.MAX_SALARY;
  END LOOP;
END EMPLOYEES_AIU;
我使用了类似的触发器来检查数据的有效性,并发现它工作得很好


共享和享受。

我认为您最终将不得不使用触发器,因为您需要引用触发器所在的同一个表中的数据,所以它需要是表触发器而不是行触发器。大概是这样的:

create materialized view emp_emp_mv
refresh complete on commit as
select 1 dummy
from emp e1, emp e2
where e1.empno != e2.empno
and e1.rank < e2.rank
and e1.sal > e2.sal;

alter table emp_emp_mv
add constraint emp_emp_mv_chk
check (1=0) deferrable;
CREATE OR REPLACE TRIGGER EMPLOYEES_AIU
  AFTER INSERT OR UPDATE ON EMPLOYEES
  -- Note: There is no FOR EACH ROW here so 'OLD' and 'NEW' values are not available
DECLARE
  strPrev_rank      EMPLOYEES.RANK%TYPE;
  nPrev_max_salary  EMPLOYEES.SALARY%TYPE;
BEGIN
  FOR aRow IN (SELECT RANK, MAX(SALARY) AS MAX_SALARY
                 FROM EMPLOYEES
                 GROUP BY RANK
                 ORDER BY RANK DESC)
  LOOP
    IF nPrev_max_salary IS NOT NULL AND
         aRow.MAX_SALARY > nPrev_max_salary THEN
      RAISE_APPLICATION_ERROR(-20101, 'Max salary (' || aRow.MAX_SALARY||
                                      ') of rank ' || aRow.RANK ||
                                      ' exceeds max salary (' || nPrev_max_salary ||
                                      ') of rank ' || strPrev_rank);
    END IF;

    strPrev_rank := aRow.RANK;
    nPrev_max_salary := aRow.MAX_SALARY;
  END LOOP;
END EMPLOYEES_AIU;
我使用了类似的触发器来检查数据的有效性,并发现它工作得很好


共享和享受。

需要是一个光标,而不是选择进入,但在其他方面很好。请注意,触发器方法通常不会在多用户环境中工作。如果我们的薪酬等级为0-1000、2000-4000和8000-10000,那么第1阶段可以添加一名薪酬为1500的rank2员工,而第2阶段可以添加一名薪酬为1750的rank3员工,然后两个阶段都可以提交。然后,当下一个人尝试插入新员工时,无论工资如何,触发器都会抛出一个错误。此外,如果员工是一个大表或经常更新,那么每次插入或更新时进行完整表扫描将非常昂贵。这很完美。多谢各位much@Justin凯夫:这两点都做得很好。在我以前使用过的情况下,所涉及的表都是小于100行的,并且每年很少更改一次,因此这些问题不需要我们太关注。考虑到这种方法是由克服ORA-04091变异表问题的需要驱动的,有没有办法在Oracle 11中使用复合触发器来解决这些问题?需要是游标,而不是选择进入,但在其他方面很好。请注意,触发器方法通常不会在多用户环境中工作。如果我们的薪酬等级为0-1000、2000-4000和8000-10000,那么第1阶段可以添加一名薪酬为1500的rank2员工,而第2阶段可以添加一名薪酬为1750的rank3员工,然后两个阶段都可以提交。然后,当下一个人尝试插入新员工时,无论工资如何,触发器都会抛出一个错误。此外,如果员工是一个大表或经常更新,那么每次插入或更新时进行完整表扫描将非常昂贵。这很完美。多谢各位much@Justin凯夫:这两点都做得很好。在我以前使用过的情况下,所涉及的表都是小于100行的,并且每年很少更改一次,因此这些问题不需要我们太关注。考虑到这种方法是由克服ORA-04091变异表问题的需要驱动的,有没有办法在Oracle 11中使用复合触发器来解决这些问题?