How to make :NEW dynamic PL/SQL - sql

This is my trigger code. But I want to dynamically reference :new attributes without writing them one by one. How can I do that?
create or replace TRIGGER test_CHANGE_TRIGGER
AFTER INSERT OR UPDATE OR DELETE
ON test
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO test_LOG
VALUES (:NEW.d, :NEW.s, :NEW.n, :NEW.v, :NEW.e, SYSDATE, USER, 'I');
END IF;
END;

You could write a sql statement to generate the insert statement for you. Copy the result and paste it in the trigger code.
Example for table emp:
SELECT
'INSERT INTO emp_log(empno,ename, job, mgr, hiredate, sal, comm, deptno, log_date, user, operation) VALUES ('||
':NEW.'||LISTAGG(column_name,' ,:NEW.') WITHIN GROUP(order by column_id)||',SYSDATE,USER,''I'')'
FROM user_tab_columns
WHERE table_name = 'EMP';
This will return
INSERT INTO emp_log(empno,ename, job, mgr, hiredate, sal, comm, deptno, log_date, user, operation) VALUES (:NEW.EMPNO ,:NEW.ENAME ,:NEW.JOB ,:NEW.MGR ,:NEW.HIREDATE ,:NEW.SAL ,:NEW.COMM ,:NEW.DEPTNO,SYSDATE,USER,'I')

Lucky you, the answer is pretty straightforward: you can't do that, and you have to name all values one-by-one.
Unfortunately, there's nothing (provided by Oracle) you could use for that purpose.
Though, you could try to create your own procedure which would use dynamic SQL and create triggers for you (query user_tab_columns to get list of all columns). Dynamic SQL doesn't scale well, is difficult to debug, but - if you think that it'll help (because there are hundreds of triggers you have to create) - go for it.

Related

table EMP is mutating, trigger/function may not see it error

I have a table EMP in my apex oracle database that contains an attribute of salary named sal. I have another table EMPSAL that has 3 attributes named averageSal, minSal, maxSal which are to be updated using triggers whenever any DML operation is performed on the EMP table's sal column. Here is the trigger used for upgrading:
create or replace trigger empsal_update_trigger
AFTER update on emp
for each row
declare
avgSal2 emp.sal%type;
minSal2 emp.sal%type;
maxSal2 emp.sal%type;
begin
select avg(sal), min(sal), max(sal) into avgSal2, minSal2, maxSal2 from emp;
delete from empsal;
insert into empsal values(avgSal2, minSal2, maxSal2);
end;
The insert and delete triggers work fine, but the update one given above gives error whenever A record is updated in EMPSAL. I have tried using before keyword instead of after but it's no use.
You don't need a row level trigger for this case, but use a statement level one. Even no need to use local variable definition through use of INSERT INTO ... SELECT... statement.
So, just remove FOR EACH ROW such as
CREATE OR REPLACE TRIGGER empsal_update_trigger AFTER UPDATE ON emp
BEGIN
DELETE empsal;
INSERT INTO empsal
SELECT AVG(sal), MIN(sal), MAX(sal)
FROM emp;
END;
/

How to create the views using trigger?

Ca you please suggest if I can create the db view using triggers in Oracle?
E.g. I have a trigger trig_cust and I want to create a view:
Create or replace view vw_cust as select * from trig_cust;
P.S. need to use this view in loop
Triggers are an even of action. You cannot create a view out of trigger.
Triggers are used to perform an action insert/ update / delete based on particular event where as views are used to select set of columns with the combination of multiple tables.
View is used to display a combined set of required columns from various tables in order to reduce the querying effort. View is majorly used for reporting purpose. You can have a trigger on the view, but not view out of trigger.
suggest if I can create the db view using triggers in Oracle?
Can you? Yes, you can. Should you? No, you shouldn't.
Anyway, just for amusement, here you go: table and its trigger which is supposed to create a view once a new row is inserted into a table. Pay attention to pragma; without it, it wouldn't work as you can't commit in a trigger. True, there's no explicit commit there, but create view is a DDL and it implicitly commits.
SQL> create table test (empno number, ename varchar2(20));
Table created.
SQL> create or replace trigger trg_ai_test
2 after insert on test
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 execute immediate 'create or replace view v_test_' || to_char(:new.empno) ||
8 ' as select * from test where empno = ' || :new.empno;
9 end;
10 /
Trigger created.
Testing:
SQL> insert into test (empno, ename) values (1, 'Little');
1 row created.
SQL> insert into test (empno, ename) values (2, 'Foot');
1 row created.
SQL> select * from v_test_1;
EMPNO ENAME
---------- --------------------
1 Little
SQL> select * from v_test_2;
EMPNO ENAME
---------- --------------------
2 Foot
SQL>

INSERT ALL - report which insert cause exception

I have to find any way to report broken insert in INSERT ALL clause.
I need know about what line exactly is invalid and insert it into special log table.
Is possible to do this?
I'm trying wrote trigger on table before and after insert but it not working if any exception is throwed.
Another idea is write procedure which convert INSERT ALL to single INSERTs and executes it in loop, after this catch exception, but I have a troubles with realization of this idea.
Yes, you can use the DML error logging clause
INSERT INTO dw_empl
SELECT employee_id, first_name, last_name, hire_date, salary, department_id
FROM employees
WHERE hire_date > sysdate - 7
LOG ERRORS INTO err_empl ('daily_load') REJECT LIMIT 25
Full details here:
https://docs.oracle.com/cd/B28359_01/server.111/b28310/tables004.htm
here is one more option
save exceptions with "save exceptions" clause
forall i in v_data_list.first .. v_data_list.last save exceptions
insert into original_table values v_data_list (i);
and then iterate through saved exceptions
exception
when others then
if sqlcode = -24381 then
for indx in 1 .. sql%bulk_exceptions.count loop
Pkg_log_err.log_error(p_Sqlcode => sql%bulk_exceptions(indx).error_code,
p_Sqlerrm => sqlerrm(-sql%bulk_exceptions(indx).error_code));
end loop;

In PL SQL I want to refactor this for loop with inserts, by using BULK COLLECT and FORALL

For performance reasons, I want to rewrite the following to use BULK COLLECT and FORALL, rather than doing the inserts in a loop. The problem I'm running in to, is that empID must be generated on each iteration of the loop, or I need to do something similar with BULK COLLECT to create a collection to use FORALL on.
...
FOR i in 1 .. numberOfEmployeesToAdd
LOOP
BEGIN
empID := EMPLOYEE_SEQ.NEXTVAL;
INSERT INTO EMPLOYEE (ID,FIRST_NAME,LAST_NAME)
VALUES (empID, 'firstNameTest', 'lastNameTest');
INSERT INTO EMPLOYEE_DEPT_ASSOC (ID, DEPT_ID, EMP_ID)
VALUES (EMPLOYEE_DEPT_ASSOC_SEQ.NEXTVAL, '247', empID);
INSERT INTO SKILLSET (ID, EMP_ID, SKILL_ID)
VALUES (SKILLSET_ASSOC.NEXTVAL, empID, '702');
END;
END LOOP;
The examples of BULK COLLECT and FORALL seem to mostly consist of creating a cursor where you do select * from [some table] and then fetch that cursor and do a BULK COLLECT. But, I need to somehow dynamically assign values within a cursor using the next contiguous 'numberOfEmployeesToAdd' number of IDs and then do a FORALL to do the inserts.
Wont this help you? If you have a nested table with your set of data, you can join it to the SELECT
INSERT INTO EMPLOYEE (ID,FIRST_NAME,LAST_NAME)
SELECT EMPLOYEE_SEQ.NEXTVAL, 'firstNameTest', 'lastNameTest'
FROM DUAL
CONNECT BY LEVEL <= numberOfEmployeesToAdd;
INSERT INTO EMPLOYEE_DEPT_ASSOC (ID, DEPT_ID, EMP_ID)
SELECT EMPLOYEE_DEPT_ASSOC_SEQ.NEXTVAL, '247', ID
FROM EMPLOYEE;
INSERT INTO SKILLSET (ID, EMP_ID, SKILL_ID)
SELECT SKILLSET_ASSOC.NEXTVAL, ID, '702'
FROM EMPLOYEE;

insert statement in oracle without using user defined function

I have table employees with columns eno, ename, job, sal, comm
and the query like
INSERT a new employee
eno as 7787,
ename as 'abc',
job as 'salesman'
sal as 2000,
comm as tax amount
this tax is the function like
CREATE OR REPLACE FUNCTION tax
( p_sal employees.sal%type
)
RETURN NUMBER
IS
v_tax employees.sal%type;
BEGIN
v_tax:= CASE
WHEN SAL> 4000 THEN SAL * 0.33
WHEN SAL >2500 THEN SAL *0.25
WHEN SAL >1500 THEN SAL * 0.20
ELSE 0
END;
RETURN v_tax
END tax;
At the INSERT statement I can't use function tax for the column comm.
Is there any other method to do this, or how can this be best achieved?
When you say
I can't use function tax for the column comm
do you mean you're not allowed to use this function, or you can't figure out how to use it?
Assuming the latter, I don't see why you shouldn't be able to use a function in an INSERT statement. You have, however, got the syntax of the INSERT statement completely wrong.
Try
INSERT INTO employee (eno, ename, job, sal, comm)
VALUES (7787, 'abc', 'salesman', 2000, tax(2000));
I don't know where amount in your INSERT statement comes from, but given that your function takes a parameter called p_sal, I'm guessing it's applied to the value in the column sal.