This cannot be done with a check constraint. It should be possible to create a materialized view that count the number of occurences of each manager, with a check constraint on the count, and that refreshes on commit on the original table. The same can be implemented with a compound trigger, as demonstrated by Littlefoot. But this is not very scalable, since the whole table needs be scanned to refresh the materialized view after each commit.
One alternative solution would be to:
create a new table that keeps track of the number of occurences of each manager, say employee_manager_cnt
set up a trigger on the employee
table to keep table employee_manager_cnt
up to date (no need to scan the whole table, just reflect the changes based on the old and new value of manager_id
)
add a check constraint to the employee_manager_cnt
that forbids values above the target count
Here is a step by step demo, which is inspired by the answer by nop77svk on this SO question
Original table:
create table employees (
employee_id number primary key,
manager_id number
);
Insert a few records:
begin
insert into employees values(1, null);
insert into employees values(2, 1);
insert into employees values(3, 1);
insert into employees values(4, 1); -- manager 1 has 3 employees
insert into employees values(5, null);
insert into employees values(6, 5); -- manager 5 has just 1 employee
end;
/
Create the new table:
create table employee_manager_cnt (
manager_id number not null primary key,
cnt number(1, 0) not null check (cnt <= 3)
);
Populate it:
insert into employee_manager_cnt(manager_id, cnt)
select manager_id, count(*)
from employees
where manager_id is not null
group by manager_id
Check the results:
MANAGER_ID CNT
1 3
5 1
Now, create the trigger:
create or replace trigger trg_employee_manager_cnt
after insert or delete or update of manager_id
on employees
for each row
begin
-- decrease the counter when an employee changes manager or is removed
if updating or deleting then
merge into employee_manager_cnt t
using dual
on ( t.manager_id = :old.manager_id )
when matched then
update set t.cnt = t.cnt - 1
delete where t.cnt = 0
;
end if;
-- increase the counter when a employee changes manager or is added
if inserting or updating then
merge into employee_manager_cnt T
using dual
on ( t.manager_id = :new.manager_id )
when matched then
update set t.cnt = t.cnt + 1
when not matched then
insert (manager_id, cnt) values (:new.manager_id, 1)
;
end if;
end;
/
Now try to add a new record that references manager 1 (who already has 3 employees)
insert into employees values(4, 1);
-- error: ORA-00001: unique constraint (FIDDLE_QOWWVSAIOXRDGYREFVKM.SYS_C00276396) violated
Meanwhile it is still possible to affect a new employee to manager 5 (he just has one employee):
insert into employees values(10, 5);
-- 1 rows affected