Can Before Insert Trigger pass null value in Oracle - oracle

I have a before insert trigger and I'm trying to define it to insert values, and null values when necessary.
I'm trying something like this
CREATE OR REPLACE TRIGGER InsertPrezimeRuk
BEFORE INSERT ON RADNICI
FOR EACH ROW
DECLARE
I_novo_pr varchar2(50);
BEGIN
SELECT prezime INTO I_novo_pr
FROM RADNICI
WHERE jmbg= :new.ruk_jmbg;
:new.prezime_rukovodioca:= I_novo_pr;
END;
And when I try to insert some null values it returns error that no data is found.
But I want to set this, when no data is discovered to simply pass a null value into the table.
And I tried something like this
CREATE OR REPLACE TRIGGER InsertPrezimeRuk
BEFORE INSERT ON RADNICI
FOR EACH ROW
DECLARE
I_novo_pr varchar2(50);
BEGIN
IF I_novo_pr is null
THEN :new.prezime_rukovodioca:= null;
END IF;
SELECT prezime INTO I_novo_pr
FROM RADNICI
WHERE jmbg= :new.ruk_jmbg;
:new.prezime_rukovodioca:= I_novo_pr;
END;
But it's still not working.
Is this possible at all? If it is, please help.

CREATE OR REPLACE TRIGGER InsertPrezimeRuk
BEFORE INSERT ON RADNICI
FOR EACH ROW
DECLARE
I_novo_pr varchar2(50);
BEGIN
SELECT prezime INTO I_novo_pr
FROM RADNICI
WHERE jmbg= :new.ruk_jmbg;
:new.prezime_rukovodioca:= I_novo_pr;
exception
when no_data_found then :new.prezime_rukovodioca:=null;
END;

Related

Using Procedure in Trigger in PL/SQL

Well I have this Procedure:
SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE check_salary (job_id employees.job_id%TYPE, salary employees.salary%TYPE)
IS
maxSal NUMBER;
minSal NUMBER;
BEGIN
SELECT MAX(salary) INTO maxSal FROM employees WHERE job_id = job_id;
SELECT MIN(salary) INTO minSal FROM employees WHERE job_id = job_id;
IF maxSal >= salary OR minSal <= salary THEN
RAISE_APPLICATION_ERROR(-20001,'Invalid slary '||salary||'. Salaries for job '||job_id||' must be between '||minSal||' and '||maxSal);
END IF;
END;
This PROCEDURE has two parameters. It checks if the salary of a job_id is between the max and the min of the job.
Now I want to create a TRIGGER.
If INSERT or UPDATE is called on employees I want to execute the prodcedure. But I don't know what I should write as a parameter in the procedure
CREATE OR REPLACE TRIGGER check_salary_trg
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
DECLARE
BEGIN
check_salary(); --Don't know which parameter I should use
END;
You should use :NEW as New Row and :OLD as Old Row.
But I think it is not useful to create a trigger as BEFORE INSERT.
You may create a trigger as AFTER INSERT OR UPDATE.
Here is an example :
CREATE OR REPLACE TRIGGER check_salary_trg
AFTER INSERT OR UPDATE ON EMPLOYEE
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
BEGIN
check_salary(:NEW.job_id);
EXCEPTION
WHEN OTHERS THEN
-- Consider logging the error and then re-raise
RAISE;
END check_salary_trg;
You just need to add the job_id and the salary
CREATE OR REPLACE TRIGGER check_salary_trg
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
DECLARE
BEGIN
check_salary(:new.job_id, :new.salary);
END;

Set a column value equal to another column with a trigger in Oracle

I have two columns, Id and Descrizione.
I want to set id = descrizione when a user insert a form. How can i do this with a trigger?
I've tried this but it not works
create or replace TRIGGER RUOLI_ID
BEFORE INSERT ON ruoli
FOR EACH ROW
BEGIN
IF :new.id IS NULL THEN
SET :new.id = Descrizione;
END IF;
END;
Close!
CREATE OR REPLACE TRIGGER ruoli_id
BEFORE INSERT
ON ruoli
FOR EACH ROW
BEGIN
:new.id := NVL (:new.id, :new.descrizione);
END;
You can use the WHEN clause which will execute the trigger only when ID is null, as follows:
CREATE OR REPLACE TRIGGER RUOLI_ID
BEFORE INSERT ON RUOLI
FOR EACH ROW
WHEN (NEW.ID IS NULL)
BEGIN
:NEW.ID := :NEW.DESCRIZIONE;
END;
Cheers!!

Oracle: Insert rowtype data into another table

I have one table called event, and created another global temp table tmp_event with the same columns and definition with event. Is it possible to insert records in event to tmp_event using this ?
DECLARE
v_record event%rowtype;
BEGIN
Insert into tmp_event values v_record;
END;
There are too many columns in event table, I want to try this because I don't want to list all the columns.
Forget to mention: I will use this in the trigger, can this v_record be the object :new after insert on EVENT table ?
To insert one row-
DECLARE
v_record event%rowtype;
BEGIN
SELECT * INTO v_record from event where rownum=1; --or whatever where clause
Insert into tmp_event values v_record;
END;
Or a more elaborate version to insert all rows from event-
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF event%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS
SELECT *
FROM event;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
-- Process contents of collection here.
Insert into tmp_event values v_record;
END LOOP;
CLOSE c_data;
END;
/
In a trigger, yes it is possible but its like the chicken or the egg. You have to initialize every field of the rowtype with the :new column values like-
v_record.col1 := :new.col1;
v_record.col2 := :new.col2;
v_record.col3 := :new.col3;
....
Apparently, the PLSQL examples above cannot be used in a trigger since it would throw a mutating trigger error. And there is no other way for you to get the entire row in the trigger other than accessing each column separately as I explain above, so if you do all this why not directly use :new.col in the INSERT into temp_event itself, will save you a lot of work.
Also since you say it's a lot of work to mention all the columns, (in Oracle 11gR2) here's a quick way of doing that by generating the INSERT statement and executing it dynamically (although not tested for performance).
CREATE OR REPLACE TRIGGER event_air --air stands for "after insert of row"
AFTER INSERT ON EVENT
FOR EACH ROW
L_query varchar2(2000); --size it appropriately
BEGIN
SELECT 'INSERT INTO tmp_event VALUES ('|| listagg (':new.'||column_name, ',')
WITHIN GROUP (ORDER BY column_name) ||')'
INTO l_query
FROM all_tab_columns
WHERE table_name='EVENT';
EXECUTE IMMEDIATE l_query;
EXCEPTION
WHEN OTHERS THEN
--Meaningful exception handling here
END;
There is a way to insert multiple rows into table with %Rowtype.
checkout below example.
DECLARE
TYPE v_test IS TABLE OF TEST_TAB%rowtype;
v_test_tab v_test ;
EXECUTE immediate ' SELECT * FROM TEST_TAB ' bulk collect INTO v_test_tab ;
dbms_output.put_line('v_test_tab.count -->'||v_test_tab.count);
FOR i IN 1..v_test_tab.count
LOOP
INSERT INTO TEST_TAB_1 VALUES v_test_tab
(i
) ;
END LOOP;
END;
sum up to full working excample ...
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF event%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS SELECT * FROM event;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
FORALL i IN 1..l_tab.count
Insert into tmp_event values l_tab(i);
commit;
END LOOP;
CLOSE c_data;
END;
/

an Easy Oracle Trigger amendment

I found a trigger in our Oracle Database
create or replace trigger pii_user_activation_trigger
before insert on pii_user_activation
for each row
begin
select seq_pii_user_activation.nextval
into :new.id
from dual;
end;
I don't want the trigger to automatically generate the id each time a row is inserted. How can I modify the trigger so that it only generates a new id if the INSERT statement does not provide a value?
create or replace trigger pii_user_activation_trigger
before insert on pii_user_activation<
for each row
begin
IF :new.id IS NULL THEN
select seq_pii_user_activation.nextval
into :new.id
from dual;
END IF;
end;
It sounds like you just want
create or replace trigger pii_user_activation_trigger
before insert on pii_user_activation
for each row
begin
IF( :new.id IS NULL )
THEN
select seq_pii_user_activation.nextval
into :new.id
from dual;
END IF;
end;
This makes the generally reasonable assumption that there is no non-NULL default value specified for the column.
Just another method:
create or replace trigger pii_user_activation_trigger
before insert on pii_user_activation
for each row
WHEN (NEW.id IS NULL)
begin
select seq_pii_user_activation.nextval
into :new.id
from dual;
end;
(note that in a WHEN clause, you don't include the : before the NEW.

Can I execute a dynamic sql statement in an after update trigger?

I am using the following statement:
query_str='SELECT :NEW.FIRST_NAME||:NEW.LAST_NAME INTO HostID
FROM INPUT_TABLE WHERE INPUT_ID='
The trigger has the following code:
EXECUTE IMMEDIATE query_str ||:NEW.INPUT_ID;
I'm getting the following error when updating INPUT_TABLE:
ORA-01008: not all variables bound
If I'm updating a record where input_id=111, I would think Oracle would just execute the following statement:
SELECT :NEW.FIRST_NAME||:NEW.LAST_NAME INTO HostID
FROM INPUT_TABLE WHERE INPUT_ID=111
Why is it having binding issues?
I'm using SQLDeveloper.
create or replace
TRIGGER DATA_DETAIL_TRIG
AFTER INSERT OR UPDATE
ON INPUT_TABLE
FOR EACH ROW
DECLARE
DATA_SOURCE_ID_RET NUMBER;
resultcount NUMBER;
query_str VARCHAR2(512);
using_cl VARCHAR2(512);
HostId VARCHAR2(256);
DATA_SOURCE_ID NUMBER;
INPUT_ID NUMBER(38,0);
AUTOGENERATE_IND VARCHAR2(1);
autogen_const varchar(200);
pragma autonomous_transaction;
CURSOR C_DATA_SOURCES IS
SELECT DATA_SOURCE_ID
FROM DATA_SOURCE_DETAIL
WHERE AUTOGENERATE_IND='Y'
AND DATA_SOURCE_REF.ACTIVE_IND='Y';
BEGIN
OPEN C_DATA_SOURCES;
LOOP
FETCH C_DATA_SOURCES INTO DATA_SOURCE_ID_RET;
EXIT WHEN C_DATA_SOURCES%NOTFOUND;
query_str:=getHostQuery( DATA_SOURCE_ID_RET);
--SELECT :FIRST_NAME||:LAST_NAME||to_char(:DOB,'yyyy/mm/dd')
From INPUT_TABLE WHERE INPUT_ID=:INPUT_ID
using_cl:=getHostUsing(DATA_SOURCE_ID_RET);
--:NEW.FIRST_NAME, :NEW.LAST_NAME, :NEW.DOB, :NEW.INPUT_ID
EXECUTE IMMEDIATE query_str INTO HostId USING using_cl;
IF INSERTING THEN
INSERT INTO DETAIL_TABLE
(
DETAIL_ID,
INPUT_ID,
HOST_ID,
DATA_SOURCE_ID,
NOTE,
DATE_MODIFIED
) VALUES
(
DETAIL_SEQ.NEXTVAL,
:NEW.INPUT_ID,
HostId,
DATA_SOURCE_ID_RET,
'Autogenerate Data Source Insert for Insert Input',
SYSDATE
);
ELSIF UPDATING THEN
SELECT COUNT(DATA_SOURCE_ID) INTO resultcount FROM DETAIL_TABLE WHERE
INPUT_ID=:NEW.INPUT_ID AND DATA_SOURCE_ID=DATA_SOURCE_ID_RET;
IF resultcount>0 THEN
UPDATE DETAIL_TABLE
SET
HOST_ID = HostId,
NOTE ='Autogenerate Data Source Update for Update Input',
DATE_MODIFIED =SYSDATE
WHERE INPUT_ID=:NEW.INPUT_ID
AND DATA_SOURCE_ID=DATA_SOURCE_ID_RET;
ELSE
INSERT INTO DETAIL_TABLE
(
DETAIL_ID,
INPUT_ID,
HOST_ID,
DATA_SOURCE_ID,
NOTE,
DATE_MODIFIED
) VALUES
(
DETAIL_SEQ.NEXTVAL,
:NEW.INPUT_ID,
HostId,
DATA_SOURCE_ID_RET,
'Autogenerate Data Source Insert for Update Input ',
SYSDATE
);
END IF;
--end if insert or update inside update
END IF;
--end IF UPDATING
END LOOP;
Close C_DATA_SOURCES;
COMMIT;
END DATA_DETAIL_TRIG;
--end trigger
Once you you placed your :new variables in quotes, Oracle looses their meaning as special trigger variables and interprets them as regular bind variables. You probably need to use USING clause with your dynamic sql. Something like
query_str:='SELECT :FIRST_NAME||:LAST_NAME
FROM INPUT_TABLE WHERE INPUT_ID=:ID';
EXECUTE IMMEDIATE query_str INTO HostID USING
:NEW.FIRST_NAME,:NEW.LAST_NAME,:NEW.INPUT_ID;
Update.
Now as more details emerge, I wonder why do you need to query at all? From what I can see, you just compose HostID from some values in INPUT_TABLE. But since you already have have trigger attached to the same table, you already have all values you need and can simply do
HostID := :NEW.FIRST_NAME||:NEW.LAST_NAME;
Your approach would only make sense if you query different table. In this case, as I mentioned, you can't quote :NEW or :OLD when you place them in USING. You may compose other value from them, though.
#Alex: Below is the trigger.
create or replace
TRIGGER DATA_DETAIL_TRIG
AFTER INSERT OR UPDATE
ON INPUT_TABLE
FOR EACH ROW
DECLARE
DATA_SOURCE_ID_RET NUMBER;
resultcount NUMBER;
query_str VARCHAR2(512);
using_cl VARCHAR2(512);
HostId VARCHAR2(256);
DATA_SOURCE_ID NUMBER;
INPUT_ID NUMBER(38,0);
AUTOGENERATE_IND VARCHAR2(1);
autogen_const varchar(200);
pragma autonomous_transaction;
CURSOR C_DATA_SOURCES IS
SELECT DATA_SOURCE_ID
FROM DATA_SOURCE_DETAIL
WHERE AUTOGENERATE_IND='Y'
AND DATA_SOURCE_REF.ACTIVE_IND='Y';
BEGIN
OPEN C_DATA_SOURCES;
LOOP
FETCH C_DATA_SOURCES INTO DATA_SOURCE_ID_RET;
EXIT WHEN C_DATA_SOURCES%NOTFOUND;
query_str:=getHostQuery( DATA_SOURCE_ID_RET);
--SELECT :FIRST_NAME||:LAST_NAME||to_char(:DOB,'yyyy/mm/dd')
From INPUT_TABLE WHERE INPUT_ID=:INPUT_ID
using_cl:=getHostUsing(DATA_SOURCE_ID_RET);
--:NEW.FIRST_NAME, :NEW.LAST_NAME, :NEW.DOB, :NEW.INPUT_ID
EXECUTE IMMEDIATE query_str INTO HostId USING using_cl;
IF INSERTING THEN
INSERT INTO DETAIL_TABLE
(
DETAIL_ID,
INPUT_ID,
HOST_ID,
DATA_SOURCE_ID,
NOTE,
DATE_MODIFIED
) VALUES
(
DETAIL_SEQ.NEXTVAL,
:NEW.INPUT_ID,
HostId,
DATA_SOURCE_ID_RET,
'Autogenerate Data Source Insert for Insert Input',
SYSDATE
);
ELSIF UPDATING THEN
SELECT COUNT(DATA_SOURCE_ID) INTO resultcount FROM DETAIL_TABLE WHERE
INPUT_ID=:NEW.INPUT_ID AND DATA_SOURCE_ID=DATA_SOURCE_ID_RET;
IF resultcount>0 THEN
UPDATE DETAIL_TABLE
SET
HOST_ID = HostId,
NOTE ='Autogenerate Data Source Update for Update Input',
DATE_MODIFIED =SYSDATE
WHERE INPUT_ID=:NEW.INPUT_ID
AND DATA_SOURCE_ID=DATA_SOURCE_ID_RET;
ELSE
INSERT INTO DETAIL_TABLE
(
DETAIL_ID,
INPUT_ID,
HOST_ID,
DATA_SOURCE_ID,
NOTE,
DATE_MODIFIED
) VALUES
(
DETAIL_SEQ.NEXTVAL,
:NEW.INPUT_ID,
HostId,
DATA_SOURCE_ID_RET,
'Autogenerate Data Source Insert for Update Input ',
SYSDATE
);
END IF;
--end if insert or update inside update
END IF;
--end IF UPDATING
END LOOP;
Close C_DATA_SOURCES;
COMMIT;
END DATA_DETAIL_TRIG;
--end trigger

Resources