Sql 在Oracle数据库中添加约束
我们希望在Oracle数据库中添加一个约束,以检查员工的工资是否符合层次结构。 员工有身份证、姓名、职级和工资。 我们有三个等级rank1,rank2,rank3,rank1优于rank2,rank2优于rank3。 添加rank2的员工时,其工资不应高于rank1员工的工资 您能告诉我们哪种解决方案是实现此约束的最佳方案吗Sql 在Oracle数据库中添加约束,sql,oracle,plsql,Sql,Oracle,Plsql,我们希望在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中使用复合触发器来解决这些问题?