SQL Trigger for View - sql

I have a table called Absence which records periods of Staff absence from work
CREATE TABLE Absence
(
absence_id_pk varchar(6) NOT NULL,
staff_id_fk varchar(6),
start_date date,
end_date date,
reason varchar(30),
PRIMARY KEY (absence_id_pk),
FOREIGN KEY (staff_id_fk) REFERENCES Full_Time_Employee(staff_id_fk)
);
and I have created a view to count the total number of days an employee has been absent like so:
CREATE VIEW employee_absence
AS
SELECT staff_id_pk,
staff.first_name,
staff.last_name,
SUM(end_date -start_date) AS "Total Days Absent"
FROM Staff, Absence
WHERE Absence.staff_id_fk = Staff.staff_id_pk
GROUP BY staff_id_pk, staff.first_name, staff.last_name
ORDER BY staff_id_pk;
I am new to Triggers and what I want to have is a Trigger that prints out a message to the screen when a Staff's total days absent > 20 days. Being completely new to Triggers, I don't have much idea how to go about this.
Any help or ideas would be greatly appreciated!

You could do it in either of the two ways:
Check constraint on the base table.
Trigger on the base table
I would chose the check constraint over the trigger, I would simply not allow an employee to enter record into the table if his absence is more than 20 days.
CHECK constraint
SQL> DROP TABLE absence PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Absence
2 (
3 absence_id_pk varchar(6) NOT NULL,
4 staff_id_fk varchar(6),
5 start_date date,
6 end_date date,
7 reason varchar(30),
8 PRIMARY KEY (absence_id_pk)
9 );
Table created.
SQL>
SQL> ALTER TABLE Absence ADD CONSTRAINT chk CHECK(end_date - start_date <= 20);
Table altered.
SQL>
SQL> INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(1, SYSDATE -20, SYSDATE);
1 row created.
SQL> INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(2, SYSDATE -21, SYSDATE);
INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(2, SYSDATE -21, SYSDATE)
*
ERROR at line 1:
ORA-02290: check constraint (LALIT.CHK) violated
SQL>
TRIGGER appraoch
SQL> DROP TABLE absence PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Absence
2 (
3 absence_id_pk varchar(6) NOT NULL,
4 staff_id_fk varchar(6),
5 start_date date,
6 end_date date,
7 reason varchar(30),
8 PRIMARY KEY (absence_id_pk)
9 );
Table created.
SQL> CREATE OR REPLACE TRIGGER trg
2 BEFORE INSERT
3 ON absence
4 FOR EACH ROW
5 BEGIN
6 IF :NEW.end_date - :NEW.start_date > 20
7 THEN
8 RAISE_APPLICATION_ERROR(-20001, 'Total days absent are more than 20');
9 END IF;
10 END;
11 /
Trigger created.
SQL>
SQL> INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(1, SYSDATE -20, SYSDATE);
1 row created.
SQL> INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(2, SYSDATE -21, SYSDATE);
INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(2, SYSDATE -21, SYSDATE)
*
ERROR at line 1:
ORA-20001: Total days absent are more than 20
ORA-06512: at "LALIT.TRG", line 4
ORA-04088: error during execution of trigger 'LALIT.TRG'
SQL>
If you still want to allow the insert, however just display message. Then, remove the RAISE_APPLICATION_ERROR and put a DBMS_OUTPUT instead.
SQL> DROP TABLE absence PURGE;
Table dropped.
SQL>
SQL> CREATE TABLE Absence
2 (
3 absence_id_pk varchar(6) NOT NULL,
4 staff_id_fk varchar(6),
5 start_date date,
6 end_date date,
7 reason varchar(30),
8 PRIMARY KEY (absence_id_pk)
9 );
Table created.
SQL> CREATE OR REPLACE TRIGGER trg
2 BEFORE INSERT
3 ON absence
4 FOR EACH ROW
5 BEGIN
6 IF :NEW.end_date - :NEW.start_date > 20
7 THEN
8 DBMS_OUTPUT.PUT_LINE('Total days absent are more than 20');
9 END IF;
10 END;
11 /
Trigger created.
SQL>
SQL> INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(1, SYSDATE -20, SYSDATE);
1 row created.
SQL> INSERT INTO absence(absence_id_pk, start_date, end_date) VALUES(2, SYSDATE -21, SYSDATE);
Total days absent are more than 20
1 row created.
SQL>

There are several problems with your solution.
Never use DBMS_OUTPUT in a trigger. This may work fine when you write it and test from your IDE, but it generally will not result in any messages getting out to any application. Triggers write to log tables.
Your view trigger only catches the problem on the next insert after the problem happens. This could be days or months afterward and absences could have become much larger than 20 by then.
There is really no reason to perform an insert to the view. You can't insert a new absence using the view because the view doesn't expose the dates. Or are you performing an INSERT just to check the days absent? In that case, why don't you just query from the view where days > 20?
Your best course of action is to have the After Insert and Update trigger on the table itself check the number of days absent and write a log entry if the days > 20. This log entry can contain any information the trigger can obtain: if the operation was an Insert or Update, the employee this effects, the user who performed the operation, the date and time the operation was performed, the number of absent days before the operation took affect, the number of absent days after the operation took affect and so on.
A scheduled job could check the log table and do something (send email, whatever) when it comes across new entries.

Related

In SQL, can I combine two or more fields into one field during the same insert statement?

For this scenario, I have a table like this: ID (Autoincrement, PK), PartType (VarChar), and DesignItemID (VarChar). I would like to combine the columns ID and PartType into column DesignItemID using a single INSERT statement.
Is this possible?
The purpose for this scenario spawns from trying to use an external SQL database for a part library in Altium Designer. Altium Designer needs a unique ID to maintain a proper link to the part that is placed and the DB. Ordinarily, an autoincrement PK could work, however, I need to keep the different types of parts in separate tables (such at resistors in a resistor table and capacitors in a capacitor table, etc.). So, if I have two or more different tables with an autoincrement PK ID column, I will have multiple IDs all starting at 1.
My proposed solution is to make a table with column ID using autoincrement for the PK, column PartType using a char or varchar, and column DesignItemID also using a char or varchar. Upon an INSERT command, I will enter the value RES for resistor or CAP for capacitor for column PartType and somehow LPAD ID to about 6 places and CONCAT with PartType to create DesignItemID RES000001 or CAP000001 for example. Both tables have 1 as PK ID, but, with the part type and padding, a unique column can be made for Altium Designer.
I understand that in a SQL admin interface, I could structure a query to create this unique piece of data, but Altium Designer requires this unique ID to be in a column.
I can accomplish this task in Access by using a calculate field, but Access is limited to number of concurrent users and cannot scale like an external SQL DB can.
Please note that I will have far more columns in the Database that corresponds to the part. I am only focusing on the columns that I do not know if what I am asking can be done.
depending on your database,
it seems you are asking for a unique number that spans across multiple tables. This could be called ultimately a GUID - if it should also be unique across databases.
this could be done with a single SEQUENCE. or you can look up GUID generators.
exporting multiple tables with such a GUID would be no problem - you just query from wherever they live and send them to your output stream.
Importing on the other hand is more difficult - since you will need to know where each GUID lives (in which table). You can do this with another table that maps each GUID to the table it belongs in.
A little bit of walking instead of just talking. Code you'll see is Oracle, but I guess other databases offer the same or similar options. Note that I don't know Altium Designer.
Question you asked was:
can I combine two or more fields into one field during the same insert statement?
Yes, you can; you already know the operator - it is concatenation. In Oracle, it is either the concat function or double pipe || operator. Here's how.
First, two sample tables (resistors and capacitors):
SQL> create table resistor
2 (id_res varchar2(10) constraint pk_res primary key,
3 name varchar2(10) not null
4 );
Table created.
SQL> create table capacitor
2 (id_cap varchar2(10) constraint pk_cap primary key,
3 name varchar2(10) not null
4 );
Table created.
Sequence will be used to create unique numbers:
SQL> create sequence seqalt;
Sequence created.
Database trigger which creates the primary key value by concatenating a constant (RES for resistors) and the sequence number, left-padded with zeros up to 7 characters in length (so that the full value length is 10 characters):
SQL> create or replace trigger trg_bi_res
2 before insert on resistor
3 for each row
4 begin
5 :new.id_res := 'RES' || lpad(seqalt.nextval, 7, '0');
6 end trg_bi_res;
7 /
Trigger created.
SQL> create or replace trigger trg_bi_cap
2 before insert on capacitor
3 for each row
4 begin
5 :new.id_cap := 'CAP' || lpad(seqalt.nextval, 7, '0');
6 end trg_bi_cap;
7 /
Trigger created.
Let's insert some rows:
SQL> insert into resistor (name) values ('resistor 1');
1 row created.
SQL> select * from resistor;
ID_RES NAME
---------- ----------
RES0000001 resistor 1
Capacitors:
SQL> insert into capacitor (name) values ('capac 1');
1 row created.
SQL> insert into capacitor (name) values ('capac 2');
1 row created.
SQL> select * From capacitor;
ID_CAP NAME
---------- ----------
CAP0000002 capac 1
CAP0000003 capac 2
My suggestion is a view instead of a new table to be used by the Altium Designer - of course, if it is possible (maybe Designer requires a table, and nothing but a table ...):
SQL> create or replace view v_altium (designitemid, name) as
2 select id_res, name from resistor
3 union all
4 select id_cap, name from capacitor;
View created.
SQL> /
View created.
SQL> select * from v_altium;
DESIGNITEM NAME
---------- ----------
RES0000001 resistor 1
CAP0000002 capac 1
CAP0000003 capac 2
You'd now make the Altium Designer read the view and - from my point of view - it should work just fine.
If it has to be a table (let's call it altium), then it would look like this:
SQL> create table altium
2 (designitemid varchar2(10) constraint pk_alt primary key,
3 name varchar2(10)
4 );
Table created.
Triggers will now be changed so that they also insert a row into the altium table (see line #7):
SQL> create or replace trigger trg_bi_res
2 before insert on resistor
3 for each row
4 begin
5 :new.id_res := 'RES' || lpad(seqalt.nextval, 7, '0');
6 insert into altium (designitemid, name) values (:new.id_res, :new.name);
7 end trg_bi_res;
8 /
Trigger created.
SQL> create or replace trigger trg_bi_cap
2 before insert on capacitor
3 for each row
4 begin
5 :new.id_cap := 'CAP' || lpad(seqalt.nextval, 7, '0');
6 insert into altium (designitemid, name) values (:new.id_cap, :new.name);
7 end trg_bi_cap;
8 /
Trigger created.
Let's try it:
SQL> insert into resistor (name) values ('resistor 4');
1 row created.
SQL> insert into resistor (name) values ('resistor 5');
1 row created.
SQL> insert into capacitor (name) values ('capac 5');
1 row created.
Altium table contents reflects contents of resistor and capacitor:
SQL> select * from altium;
DESIGNITEM NAME
---------- ----------
RES0000011 resistor 4
RES0000012 resistor 5
CAP0000013 capac 5
SQL>
However: why do I prefer a view over a table? Because consistency might suffer. What if you delete a row from the capacitor table? You'd have to delete appropriate row from the new altium table as well, and vice versa.
You can't create a foreign key constraint from the altium table to reference primary keys in other tables because as soon as you try to insert a row into the altium table that references resistor, it would fail as there's no such a primary key in capacitor. You can create constraints, but - that's pretty much useless:
SQL> drop table altium;
Table dropped.
SQL> create table altium
2 (designitemid varchar2(10) constraint pk_alt primary key,
3 name varchar2(10),
4 --
5 constraint fk_alt_res foreign key (designitemid) references resistor (id_res),
6 constraint fk_alt_cap foreign key (designitemid) references capacitor (id_cap)
7 );
Table created.
OK, table was successfully created, but - will it work?
SQL> insert into resistor (name) values ('resistor 7');
insert into resistor (name) values ('resistor 7')
*
ERROR at line 1:
ORA-02291: integrity constraint (SCOTT.FK_ALT_CAP) violated - parent key not
found
ORA-06512: at "SCOTT.TRG_BI_RES", line 3
ORA-04088: error during execution of trigger 'SCOTT.TRG_BI_RES'
SQL>
Nope, it won't as such a primary key doesn't exist in the capacitor table.
It means that you'd have to maintain consistency manually, and that's always tricky.
Therefore, if possible, use a view.

Check for constraints before deletion

I have a very similar question to this one here, but it was asked 12 years ago and I'm sure things have changed since then.
Basically, I would like to be able to check for foreign key constraints before deleting. I don't want to just do a try/catch or rollback, because I'd like to present the information on the front end to the user to tell them what they need to do in order to be able to delete the item they're trying to remove.
I would like it to be able to continue to work going forward, if new constraints are added.
In a perfect world, I would like to be able to get back a list of primary keys from the rows in the other tables that are dependent on the row being deleted.
Well, why would you complicate things for users? As far as I understood, you'd want to inform them that the can't delete a master record until they delete detail records first. If that's so, why wouldn't you let the database do it for you (them, that is)? Hint: on delete cascade.
This is what you have now (a very simplified example):
SQL> create table tmaster
2 (id_mas number constraint pk_mas primary key);
Table created.
SQL> create table tdetail
2 (id_det number constraint pk_det primary key,
3 id_mas number constraint fk_det_mas references tmaster (id_mas));
Table created.
SQL> insert all
2 into tmaster values (1)
3 into tmaster values (2)
4 --
5 into tdetail values (100, 1) -- references master 1
6 into tdetail values (101, 1) -- references master 1
7 into tdetail values (200, 2) -- references master 2
8 select * from dual;
5 rows created.
Deleting master whose details exist won't work:
SQL> delete from tmaster where id_mas = 1;
delete from tmaster where id_mas = 1
*
ERROR at line 1:
ORA-02292: integrity constraint (SCOTT.FK_DET_MAS) violated - child record
found
SQL>
You'd have to
SQL> delete from tdetail where id_mas = 1;
2 rows deleted.
SQL> delete from tmaster where id_mas = 1;
1 row deleted.
SQL>
But, as I said, let the database work. Note line #4 in create table tdetail:
SQL> create table tmaster
2 (id_mas number constraint pk_mas primary key);
Table created.
SQL> create table tdetail
2 (id_det number constraint pk_det primary key,
3 id_mas number constraint fk_det_mas references tmaster (id_mas)
4 on delete cascade); --> this
Table created.
SQL> insert all
2 into tmaster values (1)
3 into tmaster values (2)
4 --
5 into tdetail values (100, 1) -- references master 1
6 into tdetail values (101, 1) -- references master 1
7 into tdetail values (200, 2) -- references master 2
8 select * from dual;
5 rows created.
OK, let's delete master (and master only):
SQL> delete from tmaster where id_mas = 1;
1 row deleted.
Whoa, it works! I don't have to do anything about it:
SQL> select * from tmaster;
ID_MAS
----------
2
SQL> select * from tdetail;
ID_DET ID_MAS
---------- ----------
200 2
SQL>

How would I add a check constraint for making sure a date is not in the future

I can't find the syntax to prevent people from entering a date in the future. I have this but it wont work? I am only allowed to use Oracle's SQL*Plus.
CONSTRAINT dateofenrolment CHECK (dateofenrolment <= sysdate)
You cannot do this in Oracle with a check constraint. The issue is that functions used in a check constraint must be deterministic. That is, they must return the same value given the same arguments. Clearly, sysdate does not meet this requirement, because its value change every time.
Oracle imposes this restriction because the check constraint is true not only when the data is inserted (or modified) in the table, but for all time.
You can do what you want using a trigger, however. Just define an insert and update trigger to prevent bad values from going in.
As you already know, that can't be done directly:
SQL> create table test
2 (id number primary key,
3 datum date constraint ch_future check (datum <= sysdate)
4 );
datum date constraint ch_future check (datum <= sysdate)
*
ERROR at line 3:
ORA-02436: date or system variable wrongly specified in CHECK constraint
SQL>
Lucky you, there's a simple workaround: create additional dummy column which will hold sysdate as default value, and then use that column in check constraint. Not inline (as it can't be done):
SQL> create table test
2 (id number primary key,
3 dummy date default sysdate,
4 datum date constraint ch_future check (datum <= dummy)
5 );
)
*
ERROR at line 5:
ORA-02438: Column check constraint cannot reference other columns
but as an outline constraint:
SQL> create table test
2 (id number primary key,
3 dummy date default sysdate,
4 datum date,
5 --
6 constraint ch_future check (datum <= dummy)
7 );
Table created.
SQL>
Aha. Table created. Let's test it. First to check today's date:
SQL> select sysdate from dual;
SYSDATE
-------------------
03.06.2020 19:51:41
Insert date value which is in the past:
SQL> insert into test (id, datum) values (1, to_date('20.05.2020 13:30', 'dd.mm.yyyy hh24:mi'));
1 row created.
Insert date value which is in the future:
SQL> insert into test (id, datum) values (1, to_date('20.12.2020 23:30', 'dd.mm.yyyy hh24:mi'));
insert into test (id, datum) values (1, to_date('20.12.2020 23:30', 'dd.mm.yyyy hh24:mi'))
*
ERROR at line 1:
ORA-02290: check constraint (SCOTT.CH_FUTURE) violated
SQL>
So, yes - it works, if you can live with additional column in the table.

SQL Using Procedures Along With Triggers

I am trying to create some triggers and procedures to auto populate some tables in my database. I have two tables, Users and Utilities.
Users Table:
CREATE TABLE USERS (
User_id Number(38,0) NOT NULL PRIMARY KEY,
User_name char(18) NULL ,
Storage_Size varchar(18) NULL ,
Memory_Usage Number(38,0) NULL
);
Utilities Table
CREATE TABLE UTILITIES (
Utility_id Number(38,0) NOT NULL PRIMARY KEY,
Utility_Name varchar(18) NULL ,
Utility_Cost Number(38,0) NULL ,
Running char(4) NULL ,
User_id Number(38,0) NULL ,
);
Now what I would like to take place in my DB.
When a User is INSERTED into USERS table it fires off a trigger.
This trigger will then insert a bunch of Utilities into the UTILITIES table (User_ID will match the User_ID that was just created) for the User that was created.
After Utilities have been inserted into UTILITIES table I would then like to run a procedure that will SUM(Utility_Cost) in the UTILITIES table and store the SUM in USERS.Memory_Usage WHERE the USER_ID matches.
What I have created so far:
Trigger to fire after INSERT on USERS table:
CREATE OR REPLACE TRIGGER users_after_insert
AFTER INSERT ON USERS
BEGIN
INSERT INTO UTILITIES (UTILITY_NAME, RUNNING, USER_ID, UTILITY_ID, UTILITY_COST)
VALUES
('Javaw.exe', 'YES', :new.USER_ID, seq_utility_id.nextval
, round(dbms_random.value(25000, 100000)));
sum_data();
END;
Procedure to be called from trigger:
CREATE OR REPLACE PROCEDURE sum_data
IS
BEGIN
UPDATE USERS
SET MEMORY_USAGE = (SELECT SUM(UTILITY_COST)
FROM UTILITIES
WHERE USERS.USER_ID = UTILITIES.USER_ID)
WHERE USERS.User_id = :new.User_id;
END;
However when I try and INSERT into the USERS table I get:
ORA-04091: table STUDENT052.USERS is mutating, trigger/function may not see it
ORA-06512: at "STUDENT052.SUM_DATA", line 4
ORA-06512: at "STUDENT052.USERS_AFTER_INSERT", line 5
ORA-04088: error during execution of trigger 'STUDENT052.USERS_AFTER_INSERT'
Does anyone have any idea why this is happening? Any information would be greatly appreciated. Thanks!
You will have to call the procedure separately and update the table, since the data is still not committed and the procedure will try update the same table, it wont work. Apart from that I have executed the trigger and it will populate your utilities table.
SQL> CREATE TABLE USERS (
2 User_id Number(38,0) NOT NULL PRIMARY KEY,
3 User_name char(18) NULL ,
4 Storage_Size varchar(18) NULL ,
5 Memory_Usage Number(38,0) NULL
6 );
Table created.
SQL> CREATE TABLE UTILITIES (
2 Utility_id Number(38,0) NOT NULL PRIMARY KEY,
3 Utility_Name varchar(18) NULL ,
4 Utility_Cost Number(38,0) NULL ,
5 Running char(4) NULL ,
6 User_id Number(38,0) NULL
7 );
Table created.
SQL> CREATE SEQUENCE seq_utility
2 MINVALUE 1
3 MAXVALUE 100
4 START WITH 1
5 INCREMENT BY 1
6 CACHE 5;
Sequence created.
SQL> CREATE OR REPLACE PROCEDURE sum_data ( user_id number) is
2 luser_id number:=user_id;
3 BEGIN
4 UPDATE USERS
5 SET MEMORY_USAGE = (SELECT SUM(UTILITY_COST) FROM UTILITIES WHERE USERS.USER_ID =luser_id);
6 END;
7 /
Procedure created.
SQL> CREATE OR REPLACE TRIGGER users_after_insert
2 AFTER INSERT ON USERS
3 FOR EACH ROW
4 BEGIN
5 INSERT INTO UTILITIES (UTILITY_NAME, RUNNING, USER_ID, UTILITY_ID, UTILITY_COST)
6 VALUES
7 ('Javaw.exe', 'YES', :New.USER_ID, seq_utility_id.nextval, round(dbms_random.value(25000, 100000)));
8 --sum_data(:New.USER_ID);
9 END;
10
11 /
Trigger created.
SQL> insert into users (user_id,user_name) values (125,'TESTUSER');
1 row created.
SQL> select * from utilities;
UTILITY_ID UTILITY_NAME UTILITY_COST RUNN USER_ID
---------- ------------------ ------------ ---- ----------
4 Javaw.exe 68271 YES
5 Javaw.exe 62481 YES 124
10 Javaw.exe 60727 YES 125
SQL> select * from users;
USER_ID USER_NAME STORAGE_SIZE MEMORY_USAGE
---------- ------------------ ------------------ ------------
123 TESTUSER
124 TESTUSER
125 TESTUSER
EDIT
SQL> CREATE OR REPLACE TRIGGER users_after_insert
2 AFTER INSERT ON USERS
3 FOR EACH ROW
4 BEGIN
5 INSERT INTO UTILITIES (UTILITY_NAME, RUNNING, USER_ID, UTILITY_ID, UTILITY_COST)
6 VALUES
7 ('Javaw.exe', 'YES', :New.USER_ID, seq_utility_id.nextval, round(dbms_random.value(25000, 100000)));
8 commit;
9 sum_data(:New.USER_ID);
10 END;
11
12 /
Trigger created.
SQL> insert into users (user_id,user_name) values (126,'TESTUSER');
insert into users (user_id,user_name) values (126,'TESTUSER')
*
ERROR at line 1:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "SCOTT.USERS_AFTER_INSERT", line 5
ORA-04088: error during execution of trigger 'SCOTT.USERS_AFTER_INSERT'
A commit within autonomous pragma transaction:
SQL> CREATE OR REPLACE TRIGGER users_after_insert
2 AFTER INSERT ON USERS
3 FOR EACH ROW
4 declare
5 PRAGMA AUTONOMOUS_TRANSACTION;
6 BEGIN
7 commit;
8 INSERT INTO UTILITIES (UTILITY_NAME, RUNNING, USER_ID, UTILITY_ID, UTILITY_COST)
9 VALUES
10 ('Javaw.exe', 'YES', :New.USER_ID, seq_utility_id.nextval, round(dbms_random.value(25000, 100000)));
11 sum_data(:New.USER_ID);
12 END;
13
14 /
Trigger created.
SQL> insert into users (user_id,user_name) values (126,'TESTUSER');
insert into users (user_id,user_name) values (126,'TESTUSER')
*
ERROR at line 1:
ORA-06519: active autonomous transaction detected and rolled back
ORA-06512: at "SCOTT.USERS_AFTER_INSERT", line 9
ORA-04088: error during execution of trigger 'SCOTT.USERS_AFTER_INSERT'
EDIT2
SQL> create view users_views as select a.user_id,a.user_name,SUM(p.UTILITY_COST) as "memory_usage"
2 from users a,utilities p where a.user_id=p.user_id group by a.user_id,a.user_name;
View created.
SQL> select * from users_views;
USER_ID USER_NAME memory_usage
---------- ------------------ ------------
125 TESTUSER 60727
124 TESTUSER 62481
SQL>
Yes, read up on mutating triggers. You're trying to update a record being inserted at the same time, and if you're trying to insert multiple utilities, well, it becomes a real mess.
Why not instead create a view on the tables that includes a column computing the memory usage? Or try a virtual column (or function-based column) on the users table that's based on the other table. (I haven't tested that across tables, so I can't say whether it would work or not.)

PL/SQL developer how to get the row that made the insert fail?

I'm doing a method that inserts into the table which has a unique column. What I don't know is if I can access the insert value that made the insert fail.
For example:
table1(id,name, phone);
name is unique.
insert (1,a,123);
insert (2,a,1234);
What I want is when I do the second insert I to return the id value '1' without having to recur to a query.
Thank you in advance.
From oracle 10g r2 you can use log errors clause of insert command to log errors in a separate table. Here is an example:
SQL> create table test_table(
2 id number primary key,
3 col1 varchar2(7)
4 )
5 ;
Table created
-- creates a table for logging errors (table name will be prefaced with err$_)
SQL> begin dbms_errlog.create_error_log('TEST_TABLE'); end;
2 /
PL/SQL procedure successfully completed
-- violates primary key constraint
SQL> insert into test_table(id, col1)
2 ( select 1, level
3 from dual
4 connect by level <= 3)
5 log errors reject limit unlimited;
1 row inserted
SQL> commit;
SQL> select * from test_table;
ID COL1
---------- -------
1 1
SQL> select * from err$_test_table;
ORA_ERR_NUMBER$ ORA_ERR_MESG$ ORA_ERR_ROWID$ ORA_ERR_OPTYP$ ORA_ERR_TAG$ ID COL1
--------------- ------------------------------------------------------------------------------------------------------------
1 ORA-00001: unique constraint (HR.SYS_C008315) violated I 1 2
1 ORA-00001: unique constraint (HR.SYS_C008315) violated I 1 3
maybe you can write a trigger(before insert) on your table, on which insert about to happen. In this you can check if the column value(name) already exists in table.
In case it does you may insert this duplicate record in another table for further reference
Another approach is to write the insert in a procedure where the name may be checked and the duplicate name could be stored in a table.
Hope it helps