Preventing user from adding a row that has more space than the alotted space, in a self-referencing table
-
24-06-2021 - |
Question
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?
Solution
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.