简体   繁体   中英

Oracle 10g -> Create complex constraint

I should fix a problem in the DB tier, not in the code, but it's a bit complex. I googled, but couldn't find the solution :(

DB version: Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod

Okay, let's say I have a table with multiple row, but only these are interesting to us right now.

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 makes an Order ID like: AB2000S
  • OP_DAYS: marks the days on which transport takes place, ie: 1235 -> means 1st day (monday), 2nd day (tuesday) and so on... if it ships every day, it would look like: 1234567, if only ships on Monday: 1
  • VALID_FROM+VALID_TO: gives us the range where the shipment takes place. For example, if VALID_FROM: 01SEP13, VALID_TO: 14OCT13 and OP_DAYS is: 15, this means a shipment every Monday and Friday between 1st of SEPT and 14th of OCT.

For simplicity, lets call the first one "ID", 2nd one can stay OP_DAYS and "Range" the last one.

I have to make sure that no overlapping record is inserted. That means That ID AND OP_DAYS AND Range doesn't match.

  • ID match is straight forward, they are the same String.
  • OP_DAYS match means there is a common number, so for example "1234" and "456" matches in our case, but "123" and "456" wouldn't match.
  • Range match means there is an overlap in the date, so for example "01SEP13-15SEP13" and "10SEP13-01OCT13" matches, but "01SEP13-15SEP13" and "16SEP13-01OCT13" is OK.

Above criteria has to be ALL met for the DB to reject the insert of the row. So there has to ba an ID match, an OP_DAYS mats and a Range match, and in this case, the data should be rejected.

One more thing: if it makes it easier I can make the OP_DAYS 7 columns, so every day would have it's separate column. This is only if it makes it really hard or impossible to make this constraint without this change.

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 (eg 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.

here is the AFTER INSERT OR UPDATE Trigger, which check for intersection and raise an exception if one is found.

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;
/

Don't use triggers to enforce data consistency. Write a module that perform database side validation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM