Q:

Constraint using a Trigger

I am using three tables - Orders, Employee and Position. Orders has Employee_no, and Employee table has Position_no. I want to make sure that the Employee_Nos in Order table are the numbers of those employee who are in Sales. The Position_no of employees in Sales are 3, 4, 5, 6. I used Query 1 (see below) to add a check constraint. However, it doesn't like the subquery. I could use Query 2 (see below) which makes sure that the Employee_No in Orders table is in the list (the list is the employee nos of employees who are in sales). However, Query 2 wouldn't work if a new employee joins. I know this could be solved using a trigger, but not sure how. Would be glad if somebody could help me out. 

A:

I wouldn't use triggers in your case. They are difficult to deal with and you need to know how to manage them correctly.
Consider the following solution.
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;

I don't have the Oracle now to test my solution, but I think you got the idea. You select incorrect orders into check_sales and add a constraint that it should be empty.

A:

Ah, types and subtypes.
A SALES employee is a subtype of EMP. Assume the EMP primary key is EMP_ID.
You can create a new column SALES_EMP_ID in EMP and a check constraint so that it is only set where there employee is in SALES and that, if set, it must be equal to EMP_ID. And another constraint to enforce uniqueness.
Then you can create a referential integrity constraint from ORDERS to EMP.SALES_EMP_ID
See the following demo. Only the first two inserts into T_EMP will succeed - the others test the check constraints. Of the inserts into T_ORDERS, only the first (salesman) will succeed.
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);

A:

I don't do triggers often but it would basically be like this.  I might have the syntax not quite right.
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;

