Invalid identifier in trigger in my Oracle database - oracle

I want to add an entry to the table, created a trigger so that it generates an id. But I get an error.
create or replace TRIGGER EMPLOYEE_ON_INSERT
BEFORE INSERT ON "EMPLOYEE"
FOR EACH ROW
BEGIN
SELECT next_id("SEQ$SYS_BUYER_SEQUENCE".nextval, 'EMPLOYEE')
INTO :NEW."ID"
FROM dual;
END;
INSERT INTO EMPLOYEE (ID, FIRST_NAME, LAST_NAME)
SELECT KPFUDB.EMPLOYEE_ON_INSERT(), 'Ivanov', ' Ivan'
FROM dual;

You want:
create or replace TRIGGER EMPLOYEE_ON_INSERT
BEFORE INSERT ON EMPLOYEE
FOR EACH ROW
BEGIN
:NEW.ID := SEQ$SYS_BUYER_SEQUENCE.NEXTVAL;
END;
/
then do not include the ID column in the INSERT as the trigger will overwrite any value you provide (or the default if you do not provide a value):
INSERT INTO EMPLOYEE (FIRST_NAME, LAST_NAME)
SELECT 'Ivanov',' Ivan' from dual;
Or, from Oracle 12, you do not need a trigger and can use an IDENTITY column:
CREATE TABLE employee(
id NUMBER
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
first_name VARCHAR2(20),
last_name VARCHAR2(20)
);
And a similar INSERT statement.
db<>fiddle here

Related

trigger function is not executing in Oracle pl/sql before insert or update

INSERT AND UPDATE STATEMENTS IN ANYNIMOUS BLOCK SI NOT CALLING TRIGGER BEACAUSE ERROR IN MY TRIG CODE PLEASE HELP ME TO SOLVE MY MISTAKE AS I AM NOOB IN TRIGGER CONCEPT......
Question:
HR Manager wants to keep track of all manager details of every department for auditing in the future. Whenever an HR Manager assigns a new manager, the following manager details should be recorded in DEPT_MANAGER_LOG table.
department_id : department for which manager is getting assigned or getting modified
manager_id : employee_id who is being assigned as manager
start_date : date on which manager is getting assigned for a department
end_date: end_date of manager (when a manager is assigned the end_date will be null)
user_name: name of database user who is doing this modification
Whenever an HR Manager changes manager of any department, the end date of previous manager need to be updated and details of new manager need to be inserted in DEPT_MANAGER_LOG table.'''
create or replace trigger trg_mgr_log
before insert or update of manager_id on departments
for each row
CODE:
create or replace trigger trg_mgr_log
before insert or update of manager_id on departments
for each row
declare
v_dpid departments.department_id%type ;
v_mgr_id departments.manager_id%type ;
v_start_date JOB_HISTORY.START_DATE%type;
v_end_date JOB_HISTORY.END_DATE%type;
begin
/*v_dpid := :new.department_id;
select manager_id into v_mgr_id from departments where department_id = v_dpid;*/
select START_DATE, END_DATE into v_start_date, v_end_date from job_history where employee_id =
v_mgr_id;
if inserting then
v_dpid := :new.department_id;
select manager_id into v_mgr_id from departments where department_id = v_dpid;
if(v_mgr_id is null) then
insert into DEPT_MANAGER_LOG values (v_dpid, :new.manager_id,SYSTIMESTAMP,null,user);
end if;
elsif updating then
v_dpid := :OLD.department_id;
select manager_id into v_mgr_id from departments where department_id = v_dpid;
if(v_mgr_id is not null) then
insert into DEPT_MANAGER_LOG values (v_dpid, :old.manager_id,v_start_date,SYSTIMESTAMP,user);
insert into DEPT_MANAGER_LOG values (v_dpid, :new.manager_id,SYSTIMESTAMP,null,user);
end if;
end if;
end; '
You may be getting a "mutating table" error, because you're selecting from the same table that the trigger is defined on. The following should fix that issue:
create or replace trigger trg_mgr_log
before insert or update of manager_id on departments
for each row
begin
if inserting and
:new.manager_id is null
then
insert into DEPT_MANAGER_LOG values (:new.department_id, :new.manager_id, SYSTIMESTAMP, null, user);
elsif updating and
:OLD.manager_id is not null
then
declare
v_start_date JOB_HISTORY.START_DATE%type;
begin
select START_DATE
into v_start_date
from job_history
where employee_id = :new.manager_id;
insert into DEPT_MANAGER_LOG values (:OLD.department_id, :old.manager_id, v_start_date, SYSTIMESTAMP, user);
insert into DEPT_MANAGER_LOG values (:OLD.department_id, :new.manager_id, SYSTIMESTAMP, null, user);
end;
end if;
end;

Accessing old values without :OLD and in a trigger

As discussed here, I'm unable to use :OLD and :NEW on columns with collation other than USING_NLS_COMP. I'm trying to find a way around this but haven't been successful so far.
What I can do is access the new rows by using a compound trigger, saving IDs of changed rows in AFTER EACH ROW and then looping over these and selecting rows in AFTER STATEMENT. The same doesn't apply for old values unfortunately.
Here is an example of what isn't working:
CREATE TABLE example_table (
id VARCHAR2(10),
name NVARCHAR2(100)
);
CREATE TABLE log_table (
id VARCHAR2(10),
new_name NVARCHAR2(100),
old_name NVARCHAR2(100)
);
CREATE OR REPLACE TRIGGER example_trigger
AFTER UPDATE ON example_table
FOR EACH ROW BEGIN
INSERT INTO log_table VALUES(:old.id, :new.name, :old.name);
END;
INSERT INTO example_table VALUES('01', 'Daniel');
-- this works as expected
UPDATE example_table SET name = ' John' WHERE id = '01';
SELECT * FROM log_table;
DROP TABLE example_table;
CREATE TABLE example_table (
id VARCHAR2(10),
-- this is the problematic part
name NVARCHAR2(100) COLLATE XCZECH_PUNCTUATION_CI
);
INSERT INTO example_table VALUES('01', 'Daniel');
-- here nothing is inserted into log_example, if you try to
-- recompile the trigger you'll get error PLS-00049
UPDATE example_table SET name = ' John' WHERE id = '01';
SELECT * FROM log_table;
DROP TABLE example_table;
DROP TABLE log_table;
DROP TRIGGER example_trigger;
If somebody has a solution for this it would be most appreciated.

Need to populate table with Foreign and Primary key

I need to create three tables from all_tab_col system table such that schema details are in one schema_detail table, table details are in table_detail table and column details are in col_table. These three tables are to be populated simultaneously through a stored procedure, with PK(generated using SEQUENCE) in schema_detail is FK in table_detail table and PK(generated using SEQUENCE) in table_detail is FK in col_detail table.
SQL is a set based language, so I would be tempted to solve your task with three set bases steps.
Some mock up tables (just add columns for the details you are interested in):
CREATE TABLE schema_detail (
schema_id NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL,
schema_name VARCHAR2(128 BYTE) NOT NULL,
CONSTRAINT schema_detail_pk PRIMARY KEY (schema_id)
);
CREATE TABLE table_detail (
schema_id NUMBER,
table_id NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL,
table_name VARCHAR2(128 BYTE) NOT NULL,
CONSTRAINT table_detail_pk PRIMARY KEY (table_id),
CONSTRAINT table_detail_fk FOREIGN KEY (schema_id)
REFERENCES schema_detail(schema_id)
ON DELETE CASCADE
);
CREATE INDEX table_detail_schema_idx ON table_detail(schema_id);
CREATE INDEX table_detail_name_idx ON table_detail(table_name);
CREATE TABLE col_detail (
table_id NUMBER,
col_id NUMBER GENERATED ALWAYS AS IDENTITY NOT NULL,
col_name VARCHAR2(128 BYTE) NOT NULL,
CONSTRAINT col_detail_pk PRIMARY KEY (col_id),
CONSTRAINT col_detail_fk FOREIGN KEY (table_id)
REFERENCES table_detail(table_id)
ON DELETE CASCADE
);
CREATE INDEX col_detail ON col_detail(table_id);
I'd fill the table schema_detail first. PK is generated automatically:
INSERT INTO schema_detail(schema_name)
SELECT DISTINCT c.owner FROM all_tab_columns c ORDER BY owner;
SCHEMA_ID SCHEMA_NAME
1 APPQOSSYS
2 AUDSYS
3 CTXSYS
...
Next, I'd fill the tables. The schema_id needs to be looked up the the schema_detail table. Again, we let the PKs be generated automatically:
INSERT INTO table_detail(schema_id, table_name)
SELECT DISTINCT s.schema_id, c.table_name
FROM all_tab_columns c
JOIN schema_detail s ON c.owner = s.schema_name
ORDER BY table_name;
SCHEMA_ID TABLE_ID TABLE_NAME
1 8403 WLM_CLASSIFIER_PLAN
1 8404 WLM_FEATURE_USAGE
1 8405 WLM_METRICS_STREAM
...
And last, I'd fill the columns:
INSERT INTO col_detail(table_id, col_name)
SELECT DISTINCT t.table_id, c.column_name
FROM all_tab_columns c
JOIN table_detail t ON c.table_name = t.table_name
JOIN schema_detail s ON c.owner = s.schema_name
ORDER BY s.schema_id, t.table_id, c.column_name;
Does this solve your question or do you need a PL/SQL procedure?
In case you insist on a PL/SQL stored procedure, I would code it along the lines of:
CREATE OR REPLACE PROCEDURE myproc IS
schema_id schema_detail.schema_id%type;
table_id table_detail.table_id%type;
FUNCTION lookup_insert_schema (p_schema_name VARCHAR2)
RETURN NUMBER
IS
sid schema_detail.schema_id%type;
BEGIN
BEGIN
SELECT schema_id INTO sid
FROM schema_detail
WHERE schema_name = p_schema_name;
EXCEPTION WHEN NO_DATA_FOUND THEN
INSERT INTO schema_detail (schema_name)
VALUES (p_schema_name)
RETURNING schema_id INTO sid;
END;
RETURN sid;
END lookup_insert_schema;
-- lookup p_table_name in table table_detail, if not found, insert it
FUNCTION lookup_insert_table (p_schema_id NUMBER, p_table_name VARCHAR)
RETURN NUMBER
IS
tid table_detail.table_id%type;
BEGIN
BEGIN
SELECT table_id INTO tid
FROM table_detail
WHERE schema_id = p_schema_id
AND table_name = p_table_name;
EXCEPTION WHEN NO_DATA_FOUND THEN
INSERT INTO table_detail (schema_id, table_name)
VALUES (p_schema_id, p_table_name)
RETURNING table_id INTO tid;
END;
RETURN tid;
END lookup_insert_table;
BEGIN
FOR r IN (SELECT * FROM all_tab_columns)
LOOP
schema_id := lookup_insert_schema(r.owner);
table_id := lookup_insert_table(schema_id, r.table_name);
INSERT INTO col_detail (table_id, col_name)
VALUES (table_id, r.column_name);
END LOOP;
END myproc;
/

Is possible use a not updated column in trigger body?

I'm coding a DML Trigger for insert update rows in a new table. Trigger will fired only when one single column be updated but... Is possible use into trigger body other column outside UPDATE OF one_single_column clause?
AUDIT TABLE:
CREATE TABLE employees_salary_history(
user_name VARCHAR2(45) NOT NULL,
time_stamp date DEFAULT (sysdate),
employee_id NUMBER(6),
old_salary NUMBER(8, 2),
new_salary NUMBER(8, 2),
percente_raise_salary NUMBER(8, 2));
DML TRIGGER:
CREATE OR REPLACE TRIGGER update_salary_trg
AFTER
UPDATE
OF salary
ON employees23
FOR EACH ROW
DECLARE
v_user VARCHAR2(25);
BEGIN
SELECT user
INTO v_user
FROM dual;
INSERT INTO employees_salary_history
VALUES(
v_user,
sysdate,
employee_id, -- I would like add employee_id about employee that was updated,
:OLD.salary,
:NEW.salary,
TRUNC((:NEW.salary - :OLD.salary) / :OLD.salary * 100, 3)
);
END update_salary_trg;
Trigger works right when I omite "employee_id" column but I would like add employee_id as a reference about the salary updated. When I add employee_id column I receive the following error:
Errors: check compiler log
Errores para TRIGGER HR.UPDATE_SALARY_TRG:
LINE/COL ERROR
-------- ----------------------------------
8/3 PL/SQL: SQL Statement ignored
8/15 PL/SQL: ORA-00913: too many values
This is because the trigger cant reference the employee_id field. In your case New and OLD will be same . So just replace employee_id with either :OLD.employee_id or :NEW. employee_id as shown below. Hope it helps.
CREATE OR REPLACE TRIGGER update_salary_trg
AFTER
UPDATE
OF salary
ON employees23
FOR EACH ROW
DECLARE
v_user VARCHAR2(25);
BEGIN
SELECT user
INTO v_user
FROM dual;
INSERT INTO employees_salary_history --employees_salary_history
VALUES(
v_user,
sysdate,
:NEW.employee_id, -- I would like add employee_id about employee that was updated,
:OLD.salary,
:NEW.salary,
TRUNC((:NEW.salary - :OLD.salary) / :OLD.salary * 100, 3)
);
END update_salary_trg;
Yes it is possible to use any column of employees23-table in the trigger.
If the column was not changed then the values of :old and :new pseudorecords are the same.
So in your case just use :new.employee_id.
Next I recommend you to RTFM: PL/SQL Triggers. It also explains in the details when the pseudorecords are available and what values they hold.

How get another column value from the same row if we update any column of that row in oracle trigger?

TABLE NAME : TEST
COLUMNS : ID,NAME
I am using ORACLE database.
I have written one trigger after update.
If I update name column value then I want to get ID of that updated name.
Please give me any suggestion.
Thanks....
you can use :OLD check the below example
CREATE OR REPLACE TRIGGER EX_TRIGGER
AFTER UPDATE ON TAB1
BEGIN
SET VAR = :OLD.ID
END;
Oracle Setup:
CREATE TABLE table_name ( id INT, name VARCHAR2(20) );
CREATE TABLE table_name_log( prev_id INT, id INT, update_date DATE );
CREATE TRIGGER log_id
AFTER UPDATE
ON table_name
FOR EACH ROW
BEGIN
IF :old.name <> :new.name THEN
INSERT INTO table_name_log (
prev_id, id, update_date
) VALUES (
:old.id, :new.id, SYSDATE
);
END IF;
END;
/
INSERT INTO table_name
SELECT 1, 'Alf' FROM DUAL UNION ALL
SELECT 2, 'Ben' FROM DUAL UNION ALL
SELECT 3, 'Carl' FROM DUAL;
Query:
UPDATE table_name SET name = 'Ann' WHERE id = 1;
SELECT * FROM table_name_log;
Output
PREV_ID ID UPDATE_DATE
------- -- -------------------
1 1 2016-06-02 09:56:23

Resources