oracle trigger after update on specific field followed by insert - oracle

I'm creating a trigger that will insert a record into another table based on an after update and certain text being referenced in the update..
CREATE OR REPLACE TRIGGER NCATSPROD_PM.DEACTIVATE_USERS
AFTER UPDATE OF "CHANGEBY_CHAR" ON NCATSPROD_PM.PM_USER_DATA
REFERENCING NEW AS "BICSUPP"
BEGIN
INSERT INTO NCATSPROD_PM.DEACTIVATED_USERS (USERNAME, EMAIL, DEACTIVATED_DATE)
VALUES (SELECT USER_ID, EMAIL, CHANGE_DATE FROM ncatsprod_pm.pm_user_data);
END;
When i try to compile this i'm getting an error
Error(1,3): PL/SQL: SQL Statement ignored
Error(1,90): PL/SQL: ORA-00936: missing expression
A little unsure where i'm going wrong with this? Go to source on the error takes me to the AFTER UPDATE line but looking at examples i don't see what's up. I tried using SQL Developer's trigger wizard and that gave me the same result

The REFERENCING clause is just used to change the names of the built-in OLD and NEW records. OLD contains the values of the row before it was updated, and NEW holds the values after it was updated. Here's an example of how you might use them.
create table PM_USER_DATA (user_id varchar2(20), email varchar2(20), change_date date, changeby_char varchar2(20));
create table DEACTIVATED_USERS (username varchar2(20), email varchar2(20), deactivated_date date);
insert into pm_user_data values ('test', 'test#test.com', sysdate, 'SOMEUSER');
create or replace trigger deactivate_users
after update of changeby_char on pm_user_data
referencing old as o1 new as n1
for each row
begin
if :n1.changeby_char = 'BICSUPP' then
insert into deactivated_users (username, email, deactivated_date)
values (:n1.user_id, :n1.email, :n1.change_date);
end if;
end;
/
update pm_user_data set changeby_char = 'BICSUPP';
select * from deactivated_users;
/* output:
USERNAME EMAIL DEACTIVATED_DATE
-------------------- -------------------- ----------------
test test#test.com 10-OCT-17
1 row selected.
*/

Related

Trigger before delete

I created a before delete trigger:
create or replace trigger myTrigger3
before delete on emp
for each row
begin
update emp set mgr = 'Null' where mgr = :old.emp_name;
end;
Where table is
emp(emp_id integer primary key, emp_name varchar(20), mgr varchar(20))
But when I run this statement the trigger is not running.
delete from emp where emp_id = 1004;
select * from emp;
Error report -
ORA-04091: table DB20178004.EMP is mutating, trigger/function may not see it
ORA-06512: at "DB20178004.MYTRIGGER3", line 2
ORA-04088: error during execution of trigger 'DB20178004.MYTRIGGER3'
You can prefer adding a foreign key constraint with set null option instead of such a trigger. Of course you need a primary key should already been defined on emp_id column :
alter table emp
add constraint fk_mgr foreign key(mgr)
references emp(emp_id)
on delete set null;
Whenever you delete the record with an emp_id which has matching values with mgr column those will be emptied after deletion of the record with that emp_id.
But please prefer a data type(numeric) for mgr conforming with the column
emp_id such as integer to be able to define a foreign key
constraint.
Demo
By the way,
I recommend you to use soft-deletion. e.g. adding a column active to the table and
set value of it to zero whenever want to delete, and do not show the
records with active=0 on the application.
If you insisting on deletion do not filter by emp_name column, since
there can be more than one people with the common name, but using emp_id
is better by far as being unique within the table.

table NISHAN.TBL_ADMIN is mutating, trigger/function may not see it

I have a trigger named tr_admin_user_role that automatically insert values into tbl_user_role table when we perform a insert in another table called tbl_admin. There is no error at compile time but whenever I insert a value into tbl_admin table it shows me an error and error is like
This is my tbl_admin table
CREATE TABLE tbl_admin(
admin_id INTEGER,
username VARCHAR2(50) NOT NULL UNIQUE,
passwords VARCHAR2(50) NOT NULL,
email VARCHAR2(100) UNIQUE,
enabled CHAR(1) DEFAULT 1 NOT NULL,
created_at DATE DEFAULT SYSDATE NOT NULL,
CONSTRAINT pk_admin_id PRIMARY KEY(admin_id)
);
tbl_user_role table
CREATE TABLE tbl_user_role(
user_role_id INTEGER,
username VARCHAR2(50) NOT NULL,
user_role VARCHAR2(50) DEFAULT 'ROLE_ADMIN' NOT NULL,
CONSTRAINT pk_user_role_id PRIMARY KEY(user_role_id)
);
Trigger that i have created
CREATE OR REPLACE TRIGGER tr_admin_user_role
AFTER INSERT ON tbl_admin
FOR EACH ROW
DECLARE
new_username TBL_ADMIN.username%TYPE;
BEGIN
SELECT username INTO new_username FROM (
SELECT username FROM tbl_admin ORDER BY username DESC
) WHERE ROWNUM = 1;
INSERT INTO tbl_user_role(username, user_role) VALUES(new_username, 'ROLE_ADMIN');
END;
Insert statement
INSERT INTO tbl_admin(username, passwords) VALUES('nisha', 'nisha');
That's not how you fetch the newly inserted / updated / previous value of a column in a Trigger. You should use the :OLD.column_name and :NEW.column_name to refer the old and new column values.Read the documentation to understand more.
So, your Trigger could be rewritten as
CREATE OR REPLACE TRIGGER tr_admin_user_role AFTER
INSERT ON tbl_admin
FOR EACH ROW
BEGIN
INSERT INTO tbl_user_role (
username,
user_role
) VALUES (
:NEW.username,
'ROLE_ADMIN'
);
END;
/
I assume you are using another trigger to generate
admin_id and user_role_id since they are declared as PRIMARY KEYs
and you are not including them in your inserts.
Db fiddle demo
Here I've used dummy values for those columns.

ORA-04088: error during execution of trigger

I am facing following problem. I created a table with following sql in Oracle 11g release 2:
create table loc_aud
(
username varchar2(20),
audittime date,
IP VARCHAR2(30),
locno number(4),
old_city number(4),
new_city number(4)
);
This table is in sys schema. Then I created a trigger for value base auditing using following command in sys schema
CREATE OR REPLACE TRIGGER location_audit
AFTER UPDATE OF city
ON hr.locations
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
IF :old.city != :new.city THEN
INSERT INTO loc_aud
VALUES (user, sysdate, UTL_INADDR.get_host_address,
:new.location_id,:old.city,:new.city);
END IF;
END;
After that I connected with hr schema and tried to update the city column with following command:
update locations set city = 'Dhaka' where location_id = 2100;
But it is giving me following errors
update locations set city = 'Dubai' where location_id = 2100
*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "SYS.LOCATION_AUDIT", line 3
ORA-04088: error during execution of trigger 'SYS.LOCATION_AUDIT'
What am I doing wrong?
The table I created named loc_aud had a wrong datatype. The column city was varchar2 and what I tried to save in it was a number datatype. I altered the table and it worked.

ORA-02437: "primary key violated" - why can't I see duplicate ID in SQL Developer?

I would receive an error:
ORA-02437: cannot validate (%s.%s) - primary key violated
Cause: attempted to validate a primary key with duplicate values or null values
I found it was because I have a stored procedure that increments the ID, but it had failed to do so when it re-ran and had an error related to one of my datatypes. I found I now had a duplicate ID in my database table. All this made sense and I was able to easily rectify it with a DELETE FROM MyTable WHERE ID = x, where x was the offending duplicate ID. The problem I have is the only way I was able to even find the IDs that were duplicated is in the first place is because I did a SELECT * FROM MyTable WHERE ID = x -- where x was one greater than the last ID I could actually see. I found it just by an educated guess. So:
Why can't I see these duplicate IDs when I open the table in Oracle SQL Developer? It only shows the last row as the ID before the duplicates. I don't think it is because of my primary key constraint, since the first line in my stored procedure is to remove that (and put it back, at the end - probably when I got my error), and it was not present when I looked at my table.
Is there some way to make these last IDs that got inserted into the table visible, so I wouldn't have to guess or assume that the duplicate IDs are "hiding" as one greater than the last ID I have in my table, in the future? There is a commit; in my stored procedure, so they should have appeared -- unless, of course, the procedure got hung up before it could run that line of code (highly probable).
Stored procedure that runs:
create or replace
PROCEDURE PRC_MYTABLE_INTAKE(
, EMPLOYEE_ID IN NVARCHAR2
, TITLE_POSITION IN NVARCHAR2
, CREATED_DATE IN DATE
, LAST_MODIFIED IN DATE
) AS
myid integer := 0;
appid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE MYTABLE DROP CONSTRAINT MYTABLE_PK';
COMMIT;
-- assign ID to myid
SELECT ID INTO myid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
-- increment
myid := myid + 1;
-- assign APPLICATION_ID to appid
SELECT APPLICATION_ID INTO appid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
-- increment
appid := appid + 1;
-- use these ids to insert with
INSERT INTO MYTABLE (ID, APPLICATION_ID,
, EMPLOYEE_ID
, TITLE_POSITION
, CREATED_DATE
, LAST_MODIFIED
) VALUES(myid, appid,
, EMPLOYEE_ID
, TITLE_POSITION
, CREATED_DATE
, LAST_MODIFIED
);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE PASS ADD CONSTRAINT MYTABLE_PK PRIMARY KEY (ID)';
COMMIT;
END;
Here's one problem:
SELECT ID
INTO myid
FROM MYTABLE
WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE)
There is no correlation between ID and ROWID, so you're not getting the maximum current ID, you're just getting the one that happens to be on the row that is furthest from the start of a datafile with a high number.
The code you need is:
SELECT COALESCE(MAX(ID),0)
FROM MYTABLE;
Or better yet, just use a sequence.
No idea why you're dropping the PK either.
Furthermore, when you issue the query:
SELECT APPLICATION_ID INTO appid ...
... that could be for a different row than the one you already got the id for, because a change could have been committed to the table.
Of course another issue is that you can't run two instances of this procedure at the same time either.
For David Aldridge, since he wants to look at code instead of the real reason I posted my question, run this ---
CREATE TABLE YOURSCHEMA.TESTING
(
TEST_ID NVARCHAR2(100) NOT NULL
, TEST_TYPE NVARCHAR2(100) NOT NULL
, CONSTRAINT TEST_PK PRIMARY KEY
(
TEST_ID
)
ENABLE
);
create or replace
PROCEDURE PRC_TESTING_INSERT(
TEST_TYPE IN NVARCHAR2
) AS
testid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK';
COMMIT;
-- assign TEST_ID to testid
SELECT TEST_ID INTO testid FROM TESTING WHERE ROWID IN (SELECT MAX(ROWID) FROM TESTING);
-- increment
testid := testid + 1;
-- use this id to insert with
INSERT INTO TESTING (TEST_ID, TEST_TYPE) VALUES(testid, TEST_TYPE);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE TESTING ADD CONSTRAINT TEST_PK PRIMARY KEY (TEST_ID)';
COMMIT;
END;
SET serveroutput on;
DECLARE
test_type varchar(100);
BEGIN
test_type := 'dude';
YOURSCHEMA.PRC_TESTING_INSERT(test_type);
-- to verify the variable got set and procedure ran, could do:
--dbms_output.enable;
--dbms_output.put_line(test_type);
END;
Now, because there is no data in the table, the stored procedure will fail with ORA-06512: no data found. If you then try and run it again, you will get ORA-02443: cannot drop constraint - nonexistent constraint, because the EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK'; successfully dropped it, and the procedure never ran the command at the end to re-add it. This is what made me think I needed the commits, but even without them, it still will not complete the whole procedure.
To prove that the procedure DOES run, if given proper data, run this after creating the table, but before creating/running the stored procedure:
INSERT INTO TESTING (TEST_ID, TEST_TYPE)
VALUES ('1', 'hi');
And if you run the proc from a new table (not one with its constraint dropped), it will run fine.
Since mathguy didn't post this as the answer, though I'll credit him for the information...
Answer to why I can't see the duplicates is because the COMMIT does not occur in the procedure when it failed due to a datatype mismatch (which we found was actually in the application's code that sent the variable's values into this procedure, not in the stored procedure, itself). (It's also why I'll mark down anyone that says you don't have to add so many COMMIT lines in this procedure.) The commands were run in the session of the user that starts it - in my case, another session of the same DB user I was logged in with, but started from my application, instead of my SQL Developer session. It also explains why I could do a COMMIT, myself, but it did not affect the application's session - I could not commit any actions ran from another session. Had I ran a COMMIT as an OracleCommand and did an .ExecuteNonQuery on my OracleConnection right after the failure within the catch of my application, I would have seen the rows in SQL Developer without having to do a special query.
So, in short, the only way to see the items was with a direct query using WHERE ID =, find the last ID and increment it, and put it in the query.

Trigger to enter deleted entries to a new table - SQL plus

I am trying to create a trigger which will enter values into a table terminated_employees when we delete values from the nm_employees table. I have written the trigger but it does not work. Is my trigger format right? Any ideas?
CREATE TABLE nm_departments(
dept2 varchar(20),
CONSTRAINT empPK PRIMARY KEY (dept2)
);
CREATE TABLE nm_employees(
name varchar(20),
dept varchar(20),
CONSTRAINT departments FOREIGN KEY (dept) REFERENCES nm_departments (dept2)ON DELETE CASCADE
);
CREATE TABLE terminated_employees(
te_name varchar(20),
te_dept varchar(20)
);
CREATE TRIGGER term_employee AFTER DELETE ON nm_employee
FOR EACH ROW
BEGIN
INSERT INTO terminated_employees (NEW.te_name, NEW.te_dept) VALUES (OLD.name,OLD.dept)
END;
You should not be specifying the NEW. on the column names of your INSERT statement. These are the columns in the terminated_employees table, NOT the new values. i.e.
INSERT INTO terminated_employees (te_name, te_dept)
VALUES (OLD.name,OLD.dept)
You can use show errors (or show err) in SQL*Plus to see the exact error.
You have a number of problems:
Wrong table name on create trigger (missing the s)
Missing ; after instert statement
The OLD. need to have : prefix. i.e. :OLD.name

Resources