一对多关系的最大约束 - Oracle SQL
Maximum Constraint on One to Many Relationship - Oracle SQL
使用 Orcale SQL 开发人员我想绘制出员工和经理之间的关系。但是,一个经理最多只能监管3名员工。
上面我有一个员工 table,经理 ID 作为外键。这与员工 table.
具有一对多关系
是否可以将此关系限制为最大值 3?
谢谢。
也许有触发器。
create or replace trigger constraint_trigger
before insert on employee
DECLARE
x number;
begin
select count(*) into x from employee where manager_id=:new.manager_id;
if (x=3) then
raise your_exeption;
end if;
end;
为了找到 table 中每位经理的员工人数,您必须数一数,对吗?但是,如果你这样做,你会遇到 mutating table 错误,因为你不能 select from a table which are currently being改变了。
如今,我们使用复合触发器 解决了这个问题。这是一个例子:
样本table:
SQL> create table temp
2 (empid number primary key,
3 name varchar2(20),
4 mgrid number references temp (empid)
5 );
Table created.
复合触发器:
SQL> create or replace trigger trg_3emp
2 for update or insert on temp
3 compound trigger
4
5 type emprec is record (mgrid temp.mgrid%type);
6 type row_t is table of emprec index by pls_integer;
7 g_row_t row_t;
8
9 after each row is
10 begin
11 g_row_t (g_row_t.count + 1).mgrid := :new.mgrid;
12 end after each row;
13
14 after statement is
15 l_cnt number;
16 begin
17 for i in 1 .. g_row_t.count loop
18 select count(*)
19 into l_cnt
20 from temp
21 where mgrid = g_row_t(i).mgrid;
22
23 if l_cnt = 4 then
24 raise_application_error(-20000, 'No more than 3 employees per manager');
25 end if;
26 end loop;
27 end after statement;
28 end;
29 /
Trigger created.
SQL>
测试:
SQL> -- This will be the manager
SQL> insert into temp values (1, 'Little', null);
1 row created.
SQL> -- Next 3 rows will be OK
SQL> insert into temp values (2, 'Foot' , 1);
1 row created.
SQL> insert into temp values (3, 'Scott' , 1);
1 row created.
SQL> insert into temp values (4, 'Tiger' , 1);
1 row created.
SQL> -- The 4th employee for the same manager should fail
SQL> insert into temp values (5, 'Mike' , 1);
insert into temp values (5, 'Mike' , 1)
*
ERROR at line 1:
ORA-20000: No more than 3 employees per manager
ORA-06512: at "SCOTT.TRG_3EMP", line 22
ORA-04088: error during execution of trigger 'SCOTT.TRG_3EMP'
SQL> -- Someone else can be Mike's manager
SQL> insert into temp values (5, 'Mike', 2);
1 row created.
SQL>
这不能用检查约束来完成。应该可以创建一个物化视图来计算每个管理器的出现次数,对计数进行检查约束,并在提交时刷新原始 table。正如 Littlefoot 所演示的那样,可以使用复合触发器实现相同的功能。但这不是很可扩展,因为整个 table 需要在每次提交后扫描以刷新物化视图。
另一种解决方案是:
创建一个新的 table 来跟踪每个经理的出现次数,比如 employee_manager_cnt
在 employee
table 上设置触发器以保持 table employee_manager_cnt
最新(无需扫描整个 table,只是反映根据manager_id
)
的新旧值的变化
向 employee_manager_cnt
添加检查约束,禁止值超过目标计数
这是一个step by step demo, which is inspired by the answer by nop77svk on this SO question
原版table:
create table employees (
employee_id number primary key,
manager_id number
);
插入几条记录:
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;
/
新建 table:
create table employee_manager_cnt (
manager_id number not null primary key,
cnt number(1, 0) not null check (cnt <= 3)
);
填充它:
insert into employee_manager_cnt(manager_id, cnt)
select manager_id, count(*)
from employees
where manager_id is not null
group by manager_id
查看结果:
MANAGER_ID CNT
1 3
5 1
现在,创建触发器:
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;
/
现在尝试添加一条引用经理 1(已有 3 名员工)的新记录
insert into employees values(4, 1);
-- error: ORA-00001: unique constraint (FIDDLE_QOWWVSAIOXRDGYREFVKM.SYS_C00276396) violated
同时仍然有可能将新员工影响到经理 5(他只有一名员工):
insert into employees values(10, 5);
-- 1 rows affected
使用 Orcale SQL 开发人员我想绘制出员工和经理之间的关系。但是,一个经理最多只能监管3名员工。
上面我有一个员工 table,经理 ID 作为外键。这与员工 table.
具有一对多关系是否可以将此关系限制为最大值 3?
谢谢。
也许有触发器。
create or replace trigger constraint_trigger
before insert on employee
DECLARE
x number;
begin
select count(*) into x from employee where manager_id=:new.manager_id;
if (x=3) then
raise your_exeption;
end if;
end;
为了找到 table 中每位经理的员工人数,您必须数一数,对吗?但是,如果你这样做,你会遇到 mutating table 错误,因为你不能 select from a table which are currently being改变了。
如今,我们使用复合触发器 解决了这个问题。这是一个例子:
样本table:
SQL> create table temp
2 (empid number primary key,
3 name varchar2(20),
4 mgrid number references temp (empid)
5 );
Table created.
复合触发器:
SQL> create or replace trigger trg_3emp
2 for update or insert on temp
3 compound trigger
4
5 type emprec is record (mgrid temp.mgrid%type);
6 type row_t is table of emprec index by pls_integer;
7 g_row_t row_t;
8
9 after each row is
10 begin
11 g_row_t (g_row_t.count + 1).mgrid := :new.mgrid;
12 end after each row;
13
14 after statement is
15 l_cnt number;
16 begin
17 for i in 1 .. g_row_t.count loop
18 select count(*)
19 into l_cnt
20 from temp
21 where mgrid = g_row_t(i).mgrid;
22
23 if l_cnt = 4 then
24 raise_application_error(-20000, 'No more than 3 employees per manager');
25 end if;
26 end loop;
27 end after statement;
28 end;
29 /
Trigger created.
SQL>
测试:
SQL> -- This will be the manager
SQL> insert into temp values (1, 'Little', null);
1 row created.
SQL> -- Next 3 rows will be OK
SQL> insert into temp values (2, 'Foot' , 1);
1 row created.
SQL> insert into temp values (3, 'Scott' , 1);
1 row created.
SQL> insert into temp values (4, 'Tiger' , 1);
1 row created.
SQL> -- The 4th employee for the same manager should fail
SQL> insert into temp values (5, 'Mike' , 1);
insert into temp values (5, 'Mike' , 1)
*
ERROR at line 1:
ORA-20000: No more than 3 employees per manager
ORA-06512: at "SCOTT.TRG_3EMP", line 22
ORA-04088: error during execution of trigger 'SCOTT.TRG_3EMP'
SQL> -- Someone else can be Mike's manager
SQL> insert into temp values (5, 'Mike', 2);
1 row created.
SQL>
这不能用检查约束来完成。应该可以创建一个物化视图来计算每个管理器的出现次数,对计数进行检查约束,并在提交时刷新原始 table。正如 Littlefoot 所演示的那样,可以使用复合触发器实现相同的功能。但这不是很可扩展,因为整个 table 需要在每次提交后扫描以刷新物化视图。
另一种解决方案是:
创建一个新的 table 来跟踪每个经理的出现次数,比如
employee_manager_cnt
在
employee
table 上设置触发器以保持 tableemployee_manager_cnt
最新(无需扫描整个 table,只是反映根据manager_id
) 的新旧值的变化
向
employee_manager_cnt
添加检查约束,禁止值超过目标计数
这是一个step by step demo, which is inspired by the answer by nop77svk on this SO question
原版table:
create table employees (
employee_id number primary key,
manager_id number
);
插入几条记录:
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;
/
新建 table:
create table employee_manager_cnt (
manager_id number not null primary key,
cnt number(1, 0) not null check (cnt <= 3)
);
填充它:
insert into employee_manager_cnt(manager_id, cnt)
select manager_id, count(*)
from employees
where manager_id is not null
group by manager_id
查看结果:
MANAGER_ID CNT
1 3
5 1
现在,创建触发器:
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;
/
现在尝试添加一条引用经理 1(已有 3 名员工)的新记录
insert into employees values(4, 1);
-- error: ORA-00001: unique constraint (FIDDLE_QOWWVSAIOXRDGYREFVKM.SYS_C00276396) violated
同时仍然有可能将新员工影响到经理 5(他只有一名员工):
insert into employees values(10, 5);
-- 1 rows affected