0

need your help

I have 2 connected entities: library reader (subscriber) and book issues. Reader can be locked for some period in days because of breaking library rules:

DB logical scheme

I need to add check so that reader, who's lock period isn't ended yet, couldn't take a book in a library (in other words, Issue.Taken > Subscriber.Lock_Date+Subscriber.Lock_Period)

Please help, how can I do this?

MT0
  • 143,790
  • 11
  • 59
  • 117
JGDger
  • 105
  • 1
  • 5

2 Answers2

3

This should really be handled in the business logic and not at the table level.

However, you could do it using a materialized view:

CREATE TABLE subscriber (
  id          INT PRIMARY KEY,
  lock_date   DATE,
  lock_period INTERVAL DAY(5) TO SECOND
);

CREATE TABLE issue (
  id        INT PRIMARY KEY,
  subscr_id INT NOT NULL REFERENCES subscriber( id ),
  book_id   INT,
  taken     DATE,
  returned  DATE
);

CREATE MATERIALIZED VIEW LOG ON subscriber
   WITH SEQUENCE, ROWID( id, lock_date, lock_period )
   INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW LOG ON issue
   WITH SEQUENCE, ROWID( subscr_id, taken )
   INCLUDING NEW VALUES;

CREATE MATERIALIZED VIEW subscriber_issue_MV
   BUILD IMMEDIATE
   REFRESH FAST ON COMMIT
   AS SELECT s.id,
             s.lock_date,
             s.lock_period,
             i.taken
      FROM   subscriber s
             INNER JOIN
             issue i
             ON ( i.subscr_id = s.id );

ALTER TABLE subscriber_issue_MV ADD CONSTRAINT subscriber_issue__mv__chk
  CHECK (   lock_date   IS NULL
         OR lock_period IS NULL
         OR NOT taken BETWEEN lock_date AND lock_date + lock_period );
MT0
  • 143,790
  • 11
  • 59
  • 117
  • Tried it but got an error while trying to create materialized view: ORA 00439 - feature not enabled: Advanced replication. Does it mean that I cannot create materialized view for some reason? – JGDger Jun 09 '16 at 23:23
  • You can but you can't use the logs or `FAST REFRESH ON COMMIT`... which means that it could work but you would have to keep the materialized view updated by another method - which would make it impractical. – MT0 Jun 09 '16 at 23:31
  • As I said in the first line though - solving this at the table level is possible but not the best solution. A better solution would be to only allow books to be taken out via a procedure (or the business logic of the middle tier you use to access oracle) and then wrap all the business logic of checking the user is not locked inside that. – MT0 Jun 09 '16 at 23:33
1

I agree with MTO that such validations should be handled by application code (via a stored procedure). However, if you are insistent about performing this validation via the database then the following trigger will be helpful. Again, I do not recommend this solution and the best way would be to handle it using application logic.

CREATE OR REPLACE TRIGGER trg_val_lock_dt
BEFORE INSERT ON issue
FOR EACH ROW
DECLARE
    v_is_valid CHAR(1);
BEGIN

    v_is_valid := 'Y';

    SELECT 'N' INTO v_is_valid 
    FROM subscriber s
    WHERE :NEW.subscr_id = s.subscr_id
    AND :NEW.taken BETWEEN s.lock_date AND (s.lock_date + lock_period);

    RAISE_APPLICATION_ERROR(-20001,'The subscriber is locked'); 

EXCEPTION
    WHEN NO_DATA_FOUND THEN
       NULL;
END;

The above trigger will fire before every insert in the issue table. It will check if the taken date falls between the lock date and lock date + lock period (which would be the lock end date). If such a record is found then it will throw the following error and the row will not be inserted.

ORA-20001: The subscriber is locked
ORA-06512: at "RETAIL_1.TRG_VAL_LOCK_DT", line 12

If the condition is not satisfied then the no data found exception will be raised where the trigger will do nothing and the row will be inserted.

phonetic_man
  • 1,088
  • 8
  • 12
  • It works great for inserting a new line into table, but when I try to return book (or any other update) I get ORA-04091 Table is mutating error. Can I get round this error or this is trigger limitation? – JGDger Jun 10 '16 at 13:03
  • But the trigger should fire only on insert. Is it firing on update? – phonetic_man Jun 10 '16 at 15:34
  • Yes, error throws on update and I don't understand why – JGDger Jun 10 '16 at 15:55
  • Can you help me reproduce your error. I tried the following update and it ran fine on the table. Have you made any changes to the trigger. UPDATE issue SET returned = TRUNC(SYSDATE) WHERE subscr_id = 1 AND taken ='06/20/2016' – phonetic_man Jun 13 '16 at 03:39