我试图在数据库中复制包含 Intranet C# Web 应用程序的业务逻辑,以便其他数据库可以访问它并在相同的规则下工作。如果不使用 hack,这个“规则”似乎很难实施。
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
);
和
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));
业务逻辑可以概括为
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
这个逻辑可以体现在 Check 约束中还是丑陋的触发器是唯一的方法?
编辑:
- 对于 CASE_STAGE.STAGE_ID 的所有值,必须在 CASE_STAGE.ID 中找到 PREVIOUS_STAGE_ID 的值
- 应用程序不允许从 CASE_RECOMMENDATION 中删除,一旦它不再是 CURRENT(即当 CASE_STAGE.CURRENT 的值为 0 时,该阶段关闭并且不能再更改,当 = 1 时,这是活动的阶段或行现在可以更改。)
编辑:在这里使用所有优秀的想法和评论是解决这个问题的有效方法
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)
);
在没有推荐的情况下使用现有包插入 1646 阶段时,错误是
ORA-12008: error in materialized view refresh path
ORA-02290: check constraint (APPBASE.CASE_RECOMMENDATION_MV_C01) violated
ORA-06512: at line 49
任务完成!不是物化视图的目的,但比触发器更好。
如果
CASE_RECOMMENDATION.CASE_STAGE_ID
是唯一的,您可以将参照完整性与虚拟列 (11g+) 结合使用以使其成为条件:让我们检查:
如果您想在数据库中“不可见地”应用复杂的约束,您可以通过创建物化视图然后对其应用约束来实现。
在这种情况下,您可以使用 MV 外部连接
CASE_RECOMMENDATION.CASE_STAGE_ID
来实现CASE_STAGE.PREVIOUS_CASE_STAGE_ID
。然后应该检查当 时这些都不为空CASE_STAGE.STAGE_ID = 1646
,如下所示:MV 上的检查约束只会在刷新时调用,因此要成功运行,您需要确保在 COMMIT 时完成。这将增加您的提交时间处理,因此您需要牢记以下几点:
由于此解决方案在 SQL 层中实施约束,因此它克服了过程解决方案中讨论的一些并发问题。
更新
正如 Vincent 所指出的,MV 的大小可以通过仅包含 stage_id = 1646 的行来减小。可以重新编写查询以不消耗任何行,但我想不出该怎么做现在:
将业务逻辑放在数据库中是令人钦佩的,你当然应该尽可能地为这样的事情实施约束。然而,触发器并不是唯一的选择。您可以在执行插入的 PL/SQL 包中解决问题。通过这样做,您可以获得其他好处,例如减少客户端代码、减少上下文切换、自动绑定、客户端应用程序独立性等。这是一个不完整的示例(没有同时运行它所必需的锁定)。
我在您的设计中缺少像 CASE_RECOMMENDATION_LIST(CASE_STAGE_ID NUMBER(9) PRIMARY KEY) 这样的表。每个 CASE_RECOMMENDATION 都是对应的 CASE_RECOMMENDATION_LIST 的成员,这可以由外键处理。CASE_RECOMMENDATION_LIST 必须至少包含一个 CASE_RECOMMENDATION。CASE_RECOMMENDATION_LIST 的创建和删除可以通过简单的触发器处理:在创建此 CASE_STAGE_ID 的第一个 CASE_RECOMMENDATION 之前为 CASE_STAGE_ID 创建一个 CASE_RECOMMENDATION_LIST,在删除此 CASE_STGE_ID 的最后一个 CASE_RECOMMENDATION 后删除它。CASE_STAGE 最多引用一个使用 CASE_STAGE.PREVIOUS_CASE_STAGE_ID 的 CASE_RECOMMENDATION_LIST。如果 CASE_STAGE.STAGE_ID=1646,CASE_STAGE.PREVOUS_STAGE_ID 不得为空。
为 1646 个 CASE_STAGE 创建一个自己的实体(并因此创建一个表)也许是更好的方法,但我不会进一步分析它。