我有四张桌子。
PERSON DELIVERY_MAPPING GENERATION_SYSTEM DELIVERY_METHOD
------ ---------------- ----------------- ---------------
ID PERSON_ID ID ID
NAME GENERATION_SYSTEM_ID NAME NAME
DELIVERY_METHOD_ID IS_SPECIAL
示例数据:
PERSON DELIVERY_MAPPING GENERATION_SYSTEM DELIVERY_METHOD
------ ---------------- ----------------- ---------------
1. TOM 1 1 1. COLOR PRINTER 1 1. EMAIL N
2. DICK 1 2 2. BW PRINTER 1 2. POST N
3. HARRY 2 3 3. HANDWRITTEN 3 3. PIGEONS Y
DELIVERY_METHOD
包含投放新信件的方式 - EMAIL
,POST
,PIGEON
。 IS_SPECIAL
列标记记录作为特殊交付的方式。它由Y
或N
的简单值表示。只有PIGEON
是一种特殊的投放方式,即Y
,其他方式不是N
。
GENERATION_SYSTEM
包含最终打印信件的信息。示例值为COLOR PRINTER
和DOT MATRIX PRINTER
。每个GENERATION_SYSTEM
将始终使用DELIVERY_METHOD
之一传递。 GENERATION_SYSTEM
和DELIVERY_METHOD
之间有一个外键。
现在,每个PERSON
可以让他的字母由不同的GENERATION_SYSTEM
生成,因为它是多对多关系,我们有DELIVERY_MAPPING
表,这就是为什么我们两端都有外键。
到目前为止,非常好。
我需要确保如果一个人的信件是由使用特殊交付方法的系统生成的,那么就不能允许他在映射列表中拥有多个生成系统。例如,迪克不能使用彩色打印机生成他的字母,因为他已经获得了所有由鸽子发送的手写字母(这是一种特殊的交付方式)。
我如何实现这样的约束?我尝试在DELIVERY_MAPPING
表上使用before-insert-or update触发器,但这会在更新时导致变异触发器问题。
可以进一步规范这个场景吗?也许只是我没有正确地规范我的桌子。
无论哪种方式,我都很想听听你对这个问题的看法。我希望自己足够冗长(......如果你能为这篇文章提出更好的标题,那就太好了)
答案 0 :(得分:1)
对于像这样的复杂约束,我认为你需要使用触发器。我不认为变异表问题是一个问题,因为你要么做更新,要么什么都不做。
您需要担心的唯一表格是Delivery_Mapping
。在允许更改此表之前,您需要在现有表上运行查询以获取特殊数量和gs的数量:
select SUM(case when dme.is_special = 'Y' then 1 else 0 end) as NumSpecial,
count(distinct gs.id) as NumGS,
MIN(gs.id) as GSID
from delivery_mapping dm join
generation_system gs
on dm.generation_system_id = gs.id join
delivery_method dme
on gs.delivery_method_id = dme.id
where dm.person_id = PERSONID
使用此信息,您可以检查插入/更新是否可以继续。我想你需要 检查条件:
逻辑对于更新来说有点复杂。
顺便说一句,我更喜欢在存储过程中包装更新/插入/删除,因此这样的逻辑不会隐藏在触发器中。我发现调试和维护过程比处理触发器容易得多,这可能是级联的。
答案 1 :(得分:1)
除非你能保证序列化,否则我会避免基表上的触发器。
你可以使用API(最佳方式),如Gordon所说(再次确保序列化)或者如果不合适,请使用物化视图(我们不需要在此处序列化,因为检查已完成在提交):
SQL> create materialized view log on person with rowid, primary key including new values;
Materialized view log created.
SQL> create materialized view log on delivery_mapping with rowid, primary key including new values;
Materialized view log created.
SQL> create materialized view log on generation_system with rowid, primary key (delivery_method_id) including new values;
Materialized view log created.
SQL> create materialized view log on delivery_method with rowid, primary key (is_special) including new values;
Materialized view log created.
我们创建一个物化视图,以显示每个用户的特殊+非特殊链接的计数:
SQL> create materialized view check_del_method
2 refresh fast on commit
3 with primary key
4 as
5 select pers.id, count(case del_meth.is_special when 'Y' then 1 end) special_count,
6 count(case del_meth.is_special when 'N' then 1 end) non_special_count
7 from person pers
8 inner join delivery_mapping del_map
9 on pers.id = del_map.person_id
10 inner join generation_system gen
11 on gen.id = del_map.generation_system_id
12 inner join delivery_method del_meth
13 on del_meth.id = gen.delivery_method_id
14 group by pers.id;
Materialized view created.
MView定义为提交时快速刷新,因此修改后的行将在提交时重建。现在的规则是,如果特殊+非特殊计数不为零,那就是错误条件。
SQL> create trigger check_del_method_aiu
2 after insert or update on check_del_method
3 for each row
4 declare
5 begin
6 if (:new.special_count > 0 and :new.non_special_count > 0)
7 then
8 raise_application_error(-20000, 'Cannot have a mix of special and non special delivery methods for user ' || :new.id);
9 end if;
11 end;
12 /
Trigger created.
SQL> set serverout on
SQL> insert into delivery_mapping values (1, 3);
1 row created.
SQL> commit;
commit
*
ERROR at line 1:
ORA-12008: error in materialized view refresh path
ORA-20000: Cannot have a mix of special and non special delivery methods for
user 1
ORA-06512: at "TEST.CHECK_DEL_METHOD_AIU", line 6
ORA-04088: error during execution of trigger 'TEST.CHECK_DEL_METHOD_AIU'
答案 2 :(得分:0)
CREATE MATERIALIZED VIEW special_queues_mv
NOLOGGING
CACHE
BUILD IMMEDIATE
REFRESH ON COMMIT
ENABLE QUERY REWRITE
AS SELECT dmap.person_id
, SUM(DECODE(dmet.is_special, 'Y', 1, 0)) AS special_queues
, SUM(DECODE(dmet.is_special, 'N', 1, 0)) AS regular_queues
FROM delivery_mapping dmap
, generation_system gsys
, delivery_method dmet
WHERE dmap.generation_system_id = gsys.id
AND gsys.delevery_method_id = dmet.id
GROUP
BY dmap.person_id
/
ALTER MATERIALIZED VIEW special_queues_mv
ADD ( CONSTRAINT special_queues_mv_chk1 CHECK ((special_queues = 1 AND regular_queues = 0) OR ( regular_queues > 0 AND special_queues = 0 ) ) ENABLE VALIDATE)
/
我是怎么做到的。 DazzaL的答案给了我一个如何做到这一点的暗示。