1

I have a problem in a hierarchical query. I have a self-referencing table like this:

    id    parent_id    name    size
-----------------------------------------
    1       null       Ship1    50
    2        1         cabin1   10
    3        1         cabin2   30
    4        3         shelf1    5
    5        3         shelf2   20
    6       null       Ship2    50
    7        6         cabin1   10
    8        6         cabin2   30
    9        7         shelf1   15

I need to add a trigger in the database which prevents the user from adding a cabin which exceeds the remaining size of that ship.

For example, in the table, for ship 1, we have a total size of 50. There are 2 cabins present which consume the size of 40. So now we are left with 10 as the available space. We should not be able to add another cabin(s) with size>10. Anything <= 10 is fine. Similar is the case for cabins and shelves. The total size of all the shelves in a cabin should not exceed the total size allotted for that cabin.

There can be any number of ship-entries in the table (which is the root).

I know about hierarchical queries and i can traverse a tree and all but I'm finding it difficult to gather my thoughts on the problem. Could anyone please point me in the right direction so that I can add that trigger successfully?

ЯegDwight
  • 24,821
  • 10
  • 45
  • 52
Snow Leopard
  • 347
  • 3
  • 7
  • 18
  • 1
    Look up the "connect by" statement in Oracle. You need to traverse a tree, and this is the Oracle solution. – Gordon Linoff Aug 03 '12 at 13:22
  • 1
    Also, if you end up upgrading to 11gR2, oracle now also supports recursive CTE's. As an aside, do you think a hierarchical table is really the best for your needs here? I would instead have a "Ships" table, a "Cabins" table, and perhaps a "Cabin Contents" table or some such... – N West Aug 03 '12 at 13:47
  • Appreciate your reply Gordon. I know about connect by and hierarchical queries. I have worked with them a bit and i can traverse a tree easily. But I'm not getting the logic for this particular problem. – Snow Leopard Aug 03 '12 at 13:49
  • Unfortunately, I do not have the luxury to modify the task according to my comfort. Since I'm not the administrator, I have to work with the problem given to me. But thanks for your input! – Snow Leopard Aug 03 '12 at 13:51

1 Answers1

1

I agree with @N_west that you should probably have separate tables for Ships, Cabins and Shelves, for your minimalist purpose and for the ease of maintenance (archiving/purging etc.). If you want to have a Trigger to handle this then you will have to capture the data from SHIPS to a log table using a trigger on SHIPS then use the data on log table to verify against insert on SHIPS. Its not best of solution but can achieve what you want. You can have a user defined exception to handle ALERTS in your application based on the error code (20101).

SQL> CREATE TABLE LOG_SHIPS AS SELECT * FROM SHIPS;

SQL> CREATE or REPLACE TRIGGER TRG_SHIP
BEFORE INSERT ON SHIPS
FOR EACH ROW

L_count   NUMBER(10);
L_total   NUMBER(10);
e_exp     EXCEPTION;

BEGIN
     SELECT sum(size) INTO L_count
       FROM LOG_SHIPS
      WHERE parent_id = :new.parent_id;

     SELECT size INTO L_total
       FROM LOG_SHIPS
      WHERE id = :new.parent_id;

     if L_count+:new.size > L_total then
        RAISE e_exp;
     else
        INSERT INTO LOG_SHIPS VALUES (:new.id,:new.parent_id,:new.name,:new.size);
     end if;

EXCEPTION
   WHEN e_exp THEN
     RAISE_APPLICATION_ERROR (-20101,'Size entered exceeds limit.');
   WHEN others THEN
      null; -- do some meaningful exception handling here
END;
/

Another approach would be to use COMPOUND TRIGGERS only if you are using Oracle 11g.

Anjan Biswas
  • 7,746
  • 5
  • 47
  • 77
  • 2
    I don't think DBMS_OUTPUT could be a good exception handling method – hmmftg Aug 04 '12 at 18:15
  • 1
    I have edited my answer with `RAISE_APPLICATION_ERROR`. This way, the error code 20101 can be captured to alert the user with a popup message or something in the application. – Anjan Biswas Aug 04 '12 at 18:18
  • The problem is that you can hit the mutating table problem whenever you query inside a trigger a table that's in the process of being modified. – eaolson Aug 04 '12 at 18:26
  • Annjawn, Thanks a lot for the trigger code. The trigger is working just fine and solves my problem. One question - Is there a reason you told me to create the log table. What if I don't create it and work directly on my ships table? – Snow Leopard Aug 06 '12 at 06:38
  • Thank you all for your valuable insight on the problem. Being a newbie I really appreciate them since I get to learn a lot of new things from them! – Snow Leopard Aug 06 '12 at 06:39
  • Answer to your previous question @GauravOjha- If you use the SHIPS table inside the trigger, you may hit the mutating trigger error. Read about mutating trigger error for more details- http://www.dba-oracle.com/t_avoiding_mutating_table_error.htm – Anjan Biswas Aug 06 '12 at 13:57