kev*_*kio 7 oracle database-design constraint oracle-11g-r2
我试图复制包含在数据库中的 Intranet C# web 应用程序的业务逻辑,以便其他数据库可以访问它并在相同的规则下工作。这个“规则”似乎很难在不使用黑客的情况下实现。
CREATE TABLE CASE_STAGE
(
ID NUMBER(9) PRIMARY KEY NOT NULL,
STAGE_ID NUMBER(9) NOT NULL,
CASE_PHASE_ID NUMBER(9) NOT NULL,
DATE_CREATED TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP NOT NULL,
END_REASON_ID NUMBER(9),
PREVIOUS_CASE_STAGE_ID NUMBER(9),
"CURRENT" NUMBER(1) NOT NULL,
DATE_CLOSED TIMESTAMP(6) DEFAULT NULL
);
Run Code Online (Sandbox Code Playgroud)
和
CREATE TABLE CASE_RECOMMENDATION
(
CASE_ID NUMBER(9) NOT NULL,
RECOMMENDATION_ID NUMBER(9) NOT NULL,
"ORDER" NUMBER(9) NOT NULL,
DATE_CREATED TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP NOT NULL,
CASE_STAGE_ID NUMBER(9) NOT NULL
);
ALTER TABLE CASE_RECOMMENDATION ADD (
CONSTRAINT SYS_C00000
PRIMARY KEY
(CASE_ID, RECOMMENDATION_ID));
Run Code Online (Sandbox Code Playgroud)
业务逻辑可以概括为
When Inserting into CASE_STAGE
If CASE_STAGE.STAGE_ID = 1646
THEN
CASE_STAGE.PREVIOUS_STAGE_ID must be found in CASE_RECOMMENDATION.CASE_STAGE_ID
Run Code Online (Sandbox Code Playgroud)
这个逻辑可以体现在 Check 约束中还是一个丑陋的触发器是唯一的方法?
编辑:
编辑:在这里使用所有优秀的想法和评论是这个问题的有效解决方案
CREATE MATERIALIZED VIEW LOG ON CASE_STAGE
TABLESPACE USERS
STORAGE (
BUFFER_POOL DEFAULT
)
NOCACHE
LOGGING
NOPARALLEL
WITH ROWID;
CREATE MATERIALIZED VIEW LOG ON CASE_RECOMMENDATION
TABLESPACE USERS
STORAGE (
BUFFER_POOL DEFAULT
)
NOCACHE
LOGGING
NOPARALLEL
WITH ROWID;
CREATE MATERIALIZED VIEW CASE_RECOMMENDATION_MV REFRESH FAST ON COMMIT AS
SELECT
cr.ROWID cr_rowid, --necessary for fast refresh
cs.ROWID cs_rowid, --necessary for fast refresh
cr.case_id,
cs.stage_id,
cr.recommendation_id
cr.case_stage_id,
cs.previous_case_stage_id
FROM CASE_RECOMMENDATION cr,
case_stage cs
WHERE cs.previous_case_stage_id = cr.case_stage_id (+)
AND CS.PREVIOUS_CASE_STAGE_ID IS NOT NULL
AND EXTRACT (YEAR FROM CS.DATE_CREATED) > 2010 --covers non conforming legacy data
AND CR.RECOMMENDATION_ID IS NULL
AND cs.stage_id =1646;
--this last line excludes everything but problem cases due to the outer join
ALTER TABLE CASE_RECOMMENDATION_MV ADD CONSTRAINT CASE_RECOMMENDATION_ck CHECK (
(previous_case_stage_id IS NOT NULL AND case_stage_id IS NOT NULL)
);
Run Code Online (Sandbox Code Playgroud)
在没有推荐的情况下使用现有包插入 1646 阶段时,错误是
ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (APPBASE.CASE_RECOMMENDATION_MV_C01) violated
ORA-06512: at line 49
Run Code Online (Sandbox Code Playgroud)
任务完成!不是物化视图的目的,而是比触发器更好。
如果CASE_RECOMMENDATION.CASE_STAGE_ID是唯一的,您可以将参照完整性与虚拟列 (11g+) 一起使用以使其成为有条件的:
alter table CASE_RECOMMENDATION ADD CONSTRAINT unique_case_stage unique (CASE_STAGE_ID);
-- virtual column only defined when stage_id=1646
alter table CASE_STAGE add
(case_1646 as (case when stage_id=1646 then previous_case_stage_id end));
-- check that the virtual column is defined when stage_id=1646
alter table case_stage add
constraint chk_1646 check ( stage_id!=1646 or previous_case_stage_id is not null);
-- referential integrity
alter table case_stage add
constraint fk_1646 foreign key (case_1646)
references case_recommendation (case_stage_id);
Run Code Online (Sandbox Code Playgroud)
让我们检查:
SQL> insert into CASE_STAGE values (1, 1, 1, sysdate, null, null, 1, null, default);
1 row(s) inserted.
SQL> insert into CASE_RECOMMENDATION values (1, 1, 1, sysdate, 1);
1 row(s) inserted.
SQL> -- fails because previous_case_stage_id is null
SQL> insert into CASE_STAGE values (2, 1646, 1, sysdate, null, null, 1, null, default);
ORA-02290: check constraint (VNZ_TEST3.CHK_1646) violated
SQL> -- fails because previous_case_stage_id doesn't exist in CASE_RECOMMENDATION
SQL> insert into CASE_STAGE values (2, 1646, 1, sysdate, null, 2, 1, null, default);
ORA-02291: integrity constraint (VNZ_TEST3.FK_1646) violated - parent key not found
SQL> -- succeeds !
SQL> insert into CASE_STAGE values (2, 1646, 1, sysdate, null, 1, 1, null, default);
1 row(s) inserted.
Run Code Online (Sandbox Code Playgroud)
如果您希望在数据库中“不可见地”应用复杂的约束,则可以通过创建物化视图然后对其应用约束来实现。
在这种情况下,您可以使用 MV 外连接CASE_RECOMMENDATION.CASE_STAGE_ID来完成此操作CASE_STAGE.PREVIOUS_CASE_STAGE_ID。然后应该检查当 时这些都不为空CASE_STAGE.STAGE_ID = 1646,如下所示:
--necessary for fast refresh
create materialized view log on case_stage with rowid;
create materialized view log on case_recommendation with rowid;
create materialized view mv refresh fast on commit as
select
cr.rowid cr_rowid, --necessary for fast refresh
cs.rowid cs_rowid, --necessary for fast refresh
cr.case_id,
cr.recommendation_id,
case when cs.stage_id = 1646 then
'Y'
else
'N'
end do_chk,
cr.case_stage_id,
cs.previous_case_stage_id
from CASE_RECOMMENDATION cr,
case_stage cs
where cs.previous_case_stage_id = cr.case_stage_id (+);
alter table mv add constraint mv_ck check (
(do_chk = 'Y' and previous_case_stage_id is not null and case_stage_id is not null )
or
(do_chk = 'N')
);
insert into CASE_STAGE values (1, 1, 1, sysdate, null, null, 1, null);
insert into CASE_RECOMMENDATION values (1, 1, 1, sysdate, 1);
commit;
insert into CASE_STAGE values (2, 1646, 1, sysdate, null, null, 1, null);
pro fails because previous_case_stage_id is null
commit;
SQL Error: ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (CHRIS.MV_CK) violated
12008. 00000 - "error in materialized view refresh path"
insert into CASE_STAGE values (2, 1646, 1, sysdate, null, 2, 1, null);
pro fails because previous_case_stage_id doesn't exist in CASE_RECOMMENDATION'
commit;
SQL Error: ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (CHRIS.MV_CK) violated
12008. 00000 - "error in materialized view refresh path"
pro succeeds !
insert into CASE_STAGE values (2, 1646, 1, sysdate, null, 1, 1, null);
commit;
pro we can't delete stuff from case recommendation now
delete CASE_RECOMMENDATION;
commit;
SQL Error: ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (CHRIS.MV_CK) violated
12008. 00000 - "error in materialized view refresh path"
Run Code Online (Sandbox Code Playgroud)
MV 上的检查约束仅在刷新时才会被调用,因此为了使其成功工作,您需要确保这是在 COMMIT 上完成的。这将增加您的提交时间处理,因此您需要记住以下几点:
由于该解决方案在 SQL 层中实现了约束,因此它克服了过程解决方案中讨论的一些并发问题。
更新
正如 Vincent 所指出的,可以通过仅包含 stage_id = 1646 的行来减小 MV 的大小。也许可以重写查询以不消耗任何行,但我不知道如何正确地做到这一点现在:
create materialized view mv refresh fast on commit as
select
cr.rowid cr_rowid, --necessary for fast refresh
cs.rowid cs_rowid, --necessary for fast refresh
cr.case_id,
cr.recommendation_id,
cr.case_stage_id,
cs.previous_case_stage_id
from CASE_RECOMMENDATION cr,
case_stage cs
where cs.previous_case_stage_id = cr.case_stage_id (+)
and cs.stage_id = 1646;
alter table mv add constraint mv_ck check (
(previous_case_stage_id is not null and case_stage_id is not null)
);
Run Code Online (Sandbox Code Playgroud)