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.
Related
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
I need to create a trigger that can insert in an audit table which DML command- insert update delete has been used on a base table plus with machine name.
Please guide me with this.
Let me show you an example:
1.A table my_test where I will make the dml operations
2.An audit table to record the operations
3.A Trigger to capture the values interested.
SQL> create table my_test ( c1 number, c2 number );
Table created.
SQL> create table audit_my_test ( id_timestamp timestamp,
session_id number,
username varchar2(30),
machine varchar2(50),
sql_text varchar2(400),
operation_type varchar2(20)
);
Table created.
SQL> create or replace trigger my_trg_my_test after insert or delete or update on my_test
referencing new as new old as old
for each row
declare
v_sql varchar2(400);
v_usr varchar2(40);
v_ope varchar2(20);
v_ter varchar2(50);
v_sid number;
begin
select sys_context('USERENV','SESSION_USER'),
sys_context('USERENV','CURRENT_SQL'),
sys_context('USERENV','SID'),
sys_context('USERENV','HOST')
into
v_usr,
v_sql,
v_sid,
v_ter
from dual;
IF INSERTING THEN
v_ope := 'Insert';
ELSIF UPDATING THEN
v_ope := 'Update';
ELSIF DELETING THEN
v_ope := 'Delete';
END IF;
insert into audit_my_test values ( systimestamp , v_sid, v_usr, v_ter, v_sql, v_ope );
end;
/
Trigger created.
SQL> show err
No errors
Now let's make some DML operations over the table
SQL> insert into my_test values ( 1 , 1) ;
1 row created.
SQL> insert into my_test values ( 2 , 2) ;
1 row created.
SQL> commit;
Commit complete.
Verify the audit table
ID_TIMESTAMP SESSION_ID USERNAME MACHINE
--------------------------------------------------------------------------- ---------- ------------------------------ ------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATION_
----------
24-JUL-20 01.01.25.567641 PM 328 SYS scglvdoracd0006.scger.dev.corp
Insert
24-JUL-20 01.01.45.514662 PM 328 SYS scglvdoracd0006.scger.dev.corp
Insert
ID_TIMESTAMP SESSION_ID USERNAME MACHINE
--------------------------------------------------------------------------- ---------- ------------------------------ ------------------------------
SQL_TEXT
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
OPERATION_
----------
You can read all the attributes from sys_context here:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions165.htm
The problem with this solution: You will never get the SQL responsible for the operation. CURRENT_SQL in SYS_CONTEXT only works inside a procedure and when it is invoked in a Fine Grain Access (FGA) policy as a handler.
If you need the SQL responsible for the operation, you either construct a FGA policy and a handler, or you use AUDIT which is much better in this specific case.
I am trying to create a trigger that runs when I insert in table 'cuenta' and make an insert in table 'cuenta_log', one of the values of this insert is gotten by accept input.
create or replace trigger trigger_new_account
AFTER INSERT ON cuenta FOR EACH ROW
accept vstring prompt "Please enter your name: ";
declare v_line varchar2(50);
begin
v_line:= 'Hello '||'&vstring';
insert into cuentas_log (fecha,cuenta,cliente)
values (now(),:new.idcuenta,v_line);
end;
cuenta_log structure is like that:
cuenta_log
("FECHA" DATE DEFAULT (sysdate),
"CUENTA" NUMBER(38,0),
"CLIENTE" VARCHAR2(50 BYTE)
)
Trigger fires on certain event; yours, after insert into table named cuenta. It doesn't work interactively with end users. You can't expect it to prompt anyone to enter anything. Besides, accept is a SQL*Plus command, it won't work in PL/SQL.
So, here's what you might have done: sample tables first:
SQL> create table cuenta (fecha date, cuenta number);
Table created.
SQL> create table cuenta_log (fecha date, cuenta number, cliente varchar2(30));
Table created.
Trigger:
SQL> create or replace trigger trigger_new_account
2 after insert on cuenta
3 for each row
4 begin
5 insert into cuenta_log(fecha, cuenta, cliente)
6 values
7 (sysdate, :new.cuenta, 'Hello ' || user);
8 end;
9 /
Trigger created.
Testing:
SQL> insert into cuenta (fecha, cuenta) values (sysdate, 100);
1 row created.
SQL> select * From cuenta;
FECHA CUENTA
------------------- ----------
11.05.2020 12:31:17 100
SQL> select * From cuenta_log;
FECHA CUENTA CLIENTE
------------------- ---------- ------------------------------
11.05.2020 12:31:17 100 Hello SCOTT
SQL>
Minimum 3 stocks in item_master table should be maintained.
Table: item_master
create table item_master
(
item_no number(5) primary key,
name varchar(10),
stock_on_hand number(5) default 0
);
Table: item_detail
create table item_detail
(
item_no number(5) references item_master,
operation varchar(10) check(operation in('Sales','Purchase')),
quantity number(5)
);
insert into item_master values(101,'Chair',5);
insert into item_master values(102,'Sofa',4);
insert into item_master values(103,'Table',6);
I have written this trigger for maintaining minimum 3 stocks.
create or replace trigger tristk
before insert or update or delete on item_master
for each row
declare
stk number(5);
begin
select stock_on_hand into stk from item_master where item_no = :new.item_no;
if(stk<=3) then
raise_application_error(-20000,'Not enough stock');
end if;
end;
/
I have written Another trigger to automatically update item_master whenever I insert or update or delete in item_detail
CREATE OR REPLACE TRIGGER triitem AFTER
INSERT OR UPDATE OR DELETE ON item_detail
FOR EACH ROW
BEGIN
UPDATE item_master
SET
stock_on_hand =
CASE :new.operation
WHEN 'Sales' THEN stock_on_hand -:new.quantity
WHEN 'Purchase' THEN stock_on_hand +:new.quantity
END
WHERE item_no =:new.item_no;
END;
/
The error with your second trigger is that you are not supposed to run queries on the trigger owner as database has not seen the changes done by your dml that raised the trigger.
I would recommend you to avoid Triggers to implement business logic altogether if possible.
The solution to your problem is to simply refer to all the columns using the :NEW prefix. I have also simplified your update statement.
CREATE OR REPLACE TRIGGER triitem AFTER
INSERT OR UPDATE OR DELETE ON item_detail
FOR EACH ROW
BEGIN
UPDATE item_master
SET
stock_on_hand =
CASE :new.operation
WHEN 'Sales' THEN stock_on_hand -:new.quantity
WHEN 'Purchase' THEN stock_on_hand +:new.quantity
END
WHERE item_no =:new.item_no;
END;
/
I am trying to create a trigger in oracle like blow:
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT ON VC_PART_LIST FOR EACH ROW
BEGIN
SELECT VC_PART_LIST_SEQ.NEWTVAL INTO :new.SEQ_ID FROM dual;
END;
/
anyone can help me?Thx.
In sequence to fetch the next value use NEXTVAL . Below is the correct syntax
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT
ON VC_PART_LIST
FOR EACH ROW
BEGIN
SELECT VC_PART_LIST_SEQ.NEXTVAL INTO :new.SEQ_ID FROM DUAL;
END;
There are few issues in your code.
1) VC_PART_LIST_SEQ.NEWTVAL there is nothing called NEWTVAL in Oracle. It should be Nextval.
2) There is no need for a SQL statement.
See demo:
Trigger:
CREATE OR REPLACE TRIGGER TRIGGER_VC_PART_LIST
BEFORE INSERT
ON VC_PART_LIST
FOR EACH ROW
BEGIN
:new.SEQ_ID := VC_PART_LIST_SEQ.NEXTVAL;
END;
/
Table:
create table VC_PART_LIST (SEQ_ID number,
VC_VTC varchar2(20), PART_NO varchar2(20), CONSUME_QTY varchar2(20));
Sequence
create sequence VC_PART_LIST_SEQ start with 1 increment by 1;
Insertion:
INSERT INTO VC_PART_LIST (VC_VTC, PART_NO, CONSUME_QTY)
VALUES ('6175SMFGD132000D', 'SC25R136', '11');
Commit;
Selection:
SQL> select * from VC_PART_LIST;
SEQ_ID VC_VTC PART_NO CONSUME_QTY
---------- -------------------- -------------------- --------------------
1 6175SMFGD132000D SC25R136 11