1

我应该解决数据库层中的问题,而不是代码中的问题,但这有点复杂。我用谷歌搜索,但找不到解决方案:(

DB 版本:Oracle 数据库 10g 企业版版本 10.2.0.3.0 - 产品

好的,假设我有一个多行表,但现在只有这些对我们来说是有趣的。

CREATE TABLE "TRANSPORT" 
(   "O_PREFIX" VARCHAR2(3 BYTE), 
    "O_NUMBER" NUMBER(4,0), 
    "O_SUFFIX" CHAR(1 BYTE), 
    "OP_DAYS" VARCHAR2(7 BYTE), 
    "VALID_FROM" DATE, 
    "VALID_TO" DATE, 
);
  • O_PREFIX+O_NUMBER+O_SUFFIX 生成订单 ID,如:AB2000S
  • OP_DAYS:标记运输发生的日期,即:1235 -> 表示第 1 天(星期一)、第 2 天(星期二)等等......如果它每天发货,它看起来像:1234567,如果只发货周一:1
  • VALID_FROM+VALID_TO:给我们发货发生的范围。例如,如果 VALID_FROM: 01SEP13, VALID_TO: 14OCT13 和 OP_DAYS 是: 15,这意味着在 9 月 1 日和 10 月 14 日之间的每周一和周五发货。

为简单起见,我们将第一个称为“ID”,第二个可以保留 OP_DAYS,将“Range”称为最后一个。

我必须确保没有插入重叠记录。这意味着 ID AND OP_DAYS AND Range 不匹配。

  • ID 匹配很简单,它们是相同的字符串。
  • OP_DAYS 匹配意味着有一个共同的数字,例如“1234”和“456”在我们的例子中匹配,但“123”和“456”不匹配。
  • 范围匹配意味着日期有重叠,例如“01SEP13-15SEP13”和“10SEP13-01OCT13”匹配,但“01SEP13-15SEP13”和“16SEP13-01OCT13”是可以的。

数据库必须全部满足上述条件才能拒绝插入行。所以必须有一个 ID 匹配、一个 OP_DAYS 垫和一个范围匹配,在这种情况下,数据应该被拒绝。

还有一件事:如果它更容易,我可以制作 OP_DAYS 7 列,所以每天都会有单独的列。只有在没有这种改变的情况下,很难或不可能做出这种约束。

4

3 回答 3

4

You cannot enforce a rule like this with a constraint. It can be done using triggers, which will be complex to code due to having to avoid the "mutating table" issue while still ensuring transaction integrity (so not using an autonomous transaction!)

There is also a method using materialized views with constraints that I have written about here on my blog (see first example). However, this is quite experimental and may not be practical in a real database (e.g. may adversely affect performance).

The most common solution is to write PL/SQL package APIs to perform the logic and force applications to use the API rather than inserting/updating the table directly.

于 2013-09-27T14:08:27.207 回答
2

这是 AFTER INSERT OR UPDATE 触发器,它检查交叉点并在找到时引发异常。

CREATE OR REPLACE TRIGGER transport_intersection_ck_trg
  AFTER INSERT OR UPDATE 
  ON transport
DECLARE
  cnt NUMBER;
BEGIN
  SELECT count(*) 
  INTO cnt
  FROM transport t1, transport t2
  WHERE t1.rowid != t2.rowid
  AND t1.PREFIX || t1."NUMBER" || t1.SUFFIX = t2.PREFIX || t2."NUMBER" || t2.SUFFIX
  AND 1 = CASE 
            WHEN INSTR(t1.op_days, 1) > 0 AND INSTR(t2.op_days, 1) > 0  THEN 1
            WHEN INSTR(t1.op_days, 2) > 0 AND INSTR(t2.op_days, 2) > 0  THEN 1
            WHEN INSTR(t1.op_days, 3) > 0 AND INSTR(t2.op_days, 3) > 0  THEN 1
            WHEN INSTR(t1.op_days, 4) > 0 AND INSTR(t2.op_days, 4) > 0  THEN 1
            WHEN INSTR(t1.op_days, 5) > 0 AND INSTR(t2.op_days, 5) > 0  THEN 1
            WHEN INSTR(t1.op_days, 6) > 0 AND INSTR(t2.op_days, 6) > 0  THEN 1
            WHEN INSTR(t1.op_days, 7) > 0 AND INSTR(t2.op_days, 7) > 0  THEN 1
            ELSE 0
          END
  AND  t1.valid_from >= t2.valid_to
  AND  t2.valid_from >= t1.valid_to
  ;        
  IF cnt > 0 THEN
    raise_application_error(-20000, 'intersection found');
  END IF;
END;
/
于 2013-09-27T14:22:01.733 回答
1

不要使用触发器来强制数据一致性。编写一个执行数据库端验证的模块。

于 2013-09-27T14:28:09.483 回答