使用触发器进行约束

时间:2011-02-17 18:41:52

标签: sql oracle

我使用三张桌子 - 订单,员工和职位。订单有Employee_no,Employee表有Position_no。我想确保Order表中的Employee_Nos是Sales中那些员工的编号。销售中员工的Position_no是3,4,5,6。我使用查询1(见下文)添加检查约束。但是,它不喜欢子查询。我可以使用查询2(见下文),它确保Orders表中的Employee_No在列表中(列表是销售中员工的员工数)。但是,如果新员工加入,则查询2将不起作用。我知道这可以使用触发器解决,但不知道如何。如果有人可以帮助我,我会很高兴的。

3 个答案:

答案 0 :(得分:5)

我不会在你的情况下使用触发器。它们很难处理,你需要知道如何正确管理它们。

考虑以下解决方案。

create materialized view check_sales
refresh complete on commit as
select 1 dummy
from ORDERS where 
EMPLOYEE_NO IS NOT IN ( SELECT EMPLOYEE_NO FROM EMPLOYEE WHERE POSITION IN (3,4,5,6))

alter table check_sales
add constraint check_sales_empty
check (1=0) deferrable;

我现在没有Oracle来测试我的解决方案,但我认为你有了这个想法。您在check_sales中选择了错误的订单,并添加了一个应该为空的约束。

答案 1 :(得分:4)

啊,类型和子类型。

SALES员工是EMP的子类型。假设EMP主键是EMP_ID。 您可以在EMP中创建新列SALES_EMP_ID和检查约束,以便仅在雇员处于SALES中时设置它,如果设置,则它必须等于EMP_ID。另一个强制执行唯一性的约束。

然后,您可以创建从ORDERS到EMP.SALES_EMP_ID的参照完整性约束 请参阅以下演示。只有前两个插入T_EMP才会成功 - 其他插入测试检查约束。在T_ORDERS中的插入中,只有第一个(推销员)会成功。

drop table t_emp purge; 

CREATE TABLE t_emp 
  (emp_id number primary key, emp_name varchar2(20),
  dept_name varchar2(10),
  sales_emp_id number,
  constraint sales_emp_id_sales_ck check 
      ((sales_emp_id is null and dept_name != 'SALES') or 
       (dept_name = 'SALES' and sales_emp_id = emp_id and sales_emp_id is not null)),
  constraint sales_emp_id_uk unique (sales_emp_id));

insert into t_emp values (1,'Alan','SALES',1);
insert into t_emp values (2,'Bill','ACCOUNTS',null);
insert into t_emp values (3,'Chuck','ACCOUNTS',3);
insert into t_emp values (4,'Dan','SALES',null);
insert into t_emp values (5,'Ellen','SALES',6);

select * from t_emp;

create table t_orders
  (ord_id number primary key,
  salesman number,
  constraint salesman_fk foreign key (salesman) references t_emp(sales_emp_id));

insert into t_orders values (1,1);
insert into t_orders values (2,2);

答案 2 :(得分:0)

我不经常做触发器,但基本上就是这样。我的语法可能不太正确。

CREATE OR REPLACE TRIGGER check_order_employee_no
  ON INSERT INTO orders
  BEFORE EACH ROW
  AS
    match_count  INTEGER;
  BEGIN
    SELECT COUNT(*)
      INTO match_count
      FROM employee
      WHERE employee_no = :new.employee_no
        AND position IN (3,4,5,6);
    IF match_count = 0 THEN
      raise_application_error( -20000, 'Employee # for order must be for a Sales employee' );
    END IF;
  END check_order_employee_no;