an Easy Oracle Trigger amendment - oracle

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.

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!!

Can Before Insert Trigger pass null value in 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;

Create table if it does not exist, and enter one row after creating

I need to create a table if it does not exist, and when it is created add a single row to it.
I'm new to oracle and PL/SQL so I basically need an equivalent of the following T-SQL:
IF OBJECT_ID('my_table', 'U') IS NULL
BEGIN
CREATE TABLE my_table(id numeric(38,0), date datetime)
INSERT INTO my_table
VALUES (NULL, 0)
END
if you want to check table creation
DECLARE count NUMBER;
BEGIN
count := 0;
SELECT COUNT(1) INTO count from user_tables WHERE table_name= 'MY_TABLE';
IF COL_COUNT = 0 THEN
EXECUTE IMMEDIATE 'create table ....';
END IF;
END;
/
A checking for DML .please note you have to sepcify your pk columns and values.
DECLARE count NUMBER;
BEGIN
count := 0;
SELECT COUNT(1) INTO count from MY_TABLE WHERE id= 0 and name='Something';
IF COL_COUNT = 0 THEN
EXECUTE IMMEDIATE 'insert into MY_TABLE (id,name) values(0,''something'') ';
END IF;
END;
/
also note I recomand to specify columns when you insert into a table
Another approach is to use exception logic. I changed field names and types according to Oracle rules
declare
eAlreadyExists exception;
pragma exception_init(eAlreadyExists, -00955);
begin
execute immediate 'CREATE TABLE my_table(id number, dateof date)';
execute immediate 'INSERT INTO my_table VALUES (NULL, sysdate)';
exception when eAlreadyExists then
null;
end;
but may be it is not a good idea to create tables dynamically
In my opinion, you should not be creating objects on the fly. You should think about your design before implementing it.
Anyway, if you really want to do it this way, then you need to do it programmatically in PL/SQL (ab)using EXECUTE IMMEDIATE.
However, I would prefer the CTAS i.e. create table as select if you want to create a table ta once with a single row. For example,
SQL> CREATE TABLE t AS SELECT 1 id, SYSDATE dt FROM DUAL;
Table created.
SQL> SELECT * FROM t;
ID DT
---------- ---------
1 29-MAY-15
SQL>
The table is created permanently.
If you are looking for a temporary table, which you could use to store session specific data , then look at creating Global temporary table.
From documentation,
Use the CREATE GLOBAL TEMPORARY TABLE statement to create a temporary
table. The ON COMMIT clause indicates if the data in the table is
transaction-specific (the default) or session-specific
You can use NOT EXISTS with select statement:
IF NOT EXISTS(SELECT 1 FROM my_table) THEN
CREATE TABLE my_table(id NUMBER, date date);
COMMIT;
INSERT INTO my_table(id, date) values (NULL, O);
COMMIT;
END IF;
UPDATE
According to the comment, I cannot use Exist directly in PL/SQL. So this is another way to do it:
begin
select case
when exists(select 1
from my_table)
then 1
else 0
end into l_exists
from dual;
if (l_exists = 1)
then
-- anything
else
EXECUTE IMMEDIATE 'CREATE TABLE my_table(id NUMBER, date date)';
EXECUTE IMMEDIATE 'INSERT INTO my_table(id, date) values (NULL, O)';
end if;
end;

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;
/

Resources