I have the following trigger for update action.
CREATE OR REPLACE TRIGGER oferta_trigger
BEFORE UPDATE ON oferty
FOR EACH ROW
DECLARE
id_oferta number (10);
id_komis number (10);
id_test_komis number (10);
suma decimal(10,2) :=0;
CURSOR c1 IS
SELECT cena_aktualna, id_test_komis
FROM oferty
WHERE status = 'A';
BEGIN
id_oferta := :NEW.idk;
dbms_output.put_line(id_oferta);
SELECT komis_id INTO id_komis FROM oferty WHERE idk = id_oferta;
FOR i in c1 LOOP
IF i.id_test_komis = id_komis THEN
suma := suma + i.cena_aktualna;
END IF;
END LOOP;
UPDATE komisy SET wartosc_samochodow = suma WHERE idk = id_komis;
END;
During update operation.
UPDATE oferty SET status ='Z' WHERE idk =1;
I get the following error:
SQL Error: ORA-04091: table OFERTY is mutating, trigger/function may
not see it.
How to solve the problem. I think this is problem of getting id.
Here's an example based on Scott's schema. I altered the DEPT table, adding a new column (SUM_SAL) which is supposed to contain sum of all salaries in that department.
First, the good, old mutating table way.
SQL> create or replace trigger trg_sumsal
2 after update on emp
3 for each row
4 declare
5 l_sum number;
6 begin
7 select sum(sal) into l_sum
8 from emp
9 where empno = :new.empno;
10
11 update dept set sum_sal = l_sum
12 where deptno = :new.deptno;
13 end;
14 /
Trigger created.
SQL> update emp set sal = 5000 where ename = 'KING';
update emp set sal = 5000 where ename = 'KING'
*
ERROR at line 1:
ORA-04091: table SCOTT.EMP is mutating, trigger/function may not see it
ORA-06512: at "SCOTT.TRG_SUMSAL", line 4
ORA-04088: error during execution of trigger 'SCOTT.TRG_SUMSAL'
SQL>
As we already knew, that won't work.
Now, the compound trigger:
SQL> create or replace trigger trg_sumsal
2 for update or insert on emp
3 compound trigger
4
5 l_deptno emp.deptno%type;
6
7 after each row is
8 begin
9 l_deptno := :new.deptno;
10 end after each row;
11
12 after statement is
13 l_sum number;
14 begin
15 select sum(sal) into l_Sum
16 from emp
17 where deptno = l_deptno;
18
19 update dept set sum_sal = l_sum
20 where deptno = l_deptno;
21 end after statement;
22 end;
23 /
Trigger created.
SQL> update emp set sal = 10000 where ename = 'KING';
1 row updated.
SQL> select * From dept;
DEPTNO DNAME LOC SUM_SAL
---------- -------------- ------------- ----------
10 ACCOUNTING NEW YORK 13750
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Nice; that works!
[EDIT, after reading William's comment]
If several departments are affected within a single UPDATE statement, the above code won't work properly. Slightly adjusted, it looks like this & fixes that issue:
SQL> create or replace trigger trg_sumsal
2 for update or insert on emp
3 compound trigger
4
5 type t_tab is table of number;
6 l_tab t_tab := t_tab();
7
8 after each row is
9 begin
10 l_tab.extend;
11 l_tab(l_tab.last) := :new.deptno;
12 end after each row;
13
14 after statement is
15 l_sum number;
16 begin
17 for i in l_tab.first .. l_tab.last loop
18 select sum(sal) into l_Sum
19 from emp
20 where deptno = l_tab(i);
21
22 update dept set sum_sal = l_sum
23 where deptno = l_tab(i);
24 end loop;
25 end after statement;
26 end;
27 /
Trigger created.
Testing:
SQL> select * from dept;
DEPTNO DNAME LOC SUM_SAL
---------- -------------- ------------- ----------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> update emp set sal = 10000 where ename in ('SMITH', 'KING');
2 rows updated.
SQL> select * from dept;
DEPTNO DNAME LOC SUM_SAL
---------- -------------- ------------- ----------
10 ACCOUNTING NEW YORK 13750
20 RESEARCH DALLAS 15975
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Related
I've created a variable IDS TABLECLIENT.ID&type; and i fill this var. with:
OPEN V_ID;
LOOP
FETCH V_ID INTO IDS;
EXIT WHEN V_ID%NOTFOUND;
END LOOP;
CLOSE V_ID;
This works fine. it stores 5 id clients but when i use this in a select statement, i'm waiting 5 registers but i only get 1:
SELECT *
FROM TABLECLIENT
WHERE ID IN IDS;
Maybe i have to loop ids inside the statement? please help oracle friends
IDS - at a moment - contains only one row fetched by the cursor.
For example, this is table of departments in Scott's sample schema:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
This code simulates what you have (though, it would have been better if you posted that info); cursor selects department numbers and - later - uses that value in another query.
SQL> set serveroutput on
SQL> declare
2 cursor v_id is select deptno from dept;
3 ids dept.deptno%type;
4 l_cnt number;
5 begin
6 open v_id;
7 loop
8 fetch v_id into ids;
9 exit when v_id%notfound;
10
11 -- display IDS's contents:
12 dbms_output.put_line('Department ' || ids);
13
14 -- you can do "something" with that value; for example,
15 -- count employees who work in that department
16 select count(*)
17 into l_cnt
18 from emp
19 where deptno = ids;
20 dbms_output.put_line('...Number of employees in DEPT ' || ids ||
21 ' = ' || l_cnt);
22 end loop;
23 close v_id;
24 end;
25 /
Result is:
Department 10 --> this is value fetched in the 1st loop round
...Number of employees in DEPT 10 = 3
Department 20 --> fetched in the 2nd round
...Number of employees in DEPT 20 = 5
Department 30
...Number of employees in DEPT 30 = 6
Department 40
...Number of employees in DEPT 40 = 0
PL/SQL procedure successfully completed.
SQL>
DECLARE
v_annual_salary NUMBER;
BEGIN
SELECT SAL * 12 INTO v_annual_salary
FROM EMP,DEPT
WHERE EMPNO = 7722;
DBMS_OUTPUT.PUT_LINE ('Annual Salary of 7722 is ' || TO_CHAR(v_annual_salary));
END;
/
Unless DEPT table contains a single row, this query will return TOO_MANY_ROWS. Why do you cross-join EMP and DEPT? Should have been just
SQL> declare
2 v_annual_salary number;
3 begin
4 select sal * 12
5 into v_annual_salary
6 from emp
7 where empno = 7934;
8
9 dbms_output.put_line('Annual salary of 7934 is ' || to_char(v_annual_salary));
10 end;
11 /
Annual salary of 7934 is 15600
PL/SQL procedure successfully completed.
SQL>
(My EMP table doesn't have employee whose EMPNO = 7722 so I used 7934).
I'm stuck on getting my stored procedure to work because I made a VARRAY data type to store all information about allergies, my goal is to input an integer primary key and the result will return the information respected to that primary key.
CREATE OR REPLACE PROCEDURE showAllergies
(
P_PID IN NUMBER,
P_f_name OUT VARCHAR2,
P_l_name OUT VARCHAR2,
P_allergies OUT allergy_ty
)
AS
BEGIN
SELECT p.name.f_name,
p.name.l_name,
al.allergies
INTO P_f_name,
P_l_name,
P_allergies
FROM PATIENT_obj_table p
INNER JOIN PATIENT_allergies al ON p.PID = al.PID
WHERE
al.PID = P_PID;
END showAllergies;
--Must do this in order to execute multiple output parameters
set serveroutput ON;
DECLARE
fname VARCHAR2(25);
lname VARCHAR2(25);
allergies allergy_ty;
total NUMBER(2,0);
begin
--select value(a) INTO allergies FROM PATIENT_allergies WHERE a.PID = 100;
total := allergies.limit;
showAllergies(101, fname, lname, allergies);
DBMS_OUTPUT.PUT_LINE('firstName: '||fname||', lastName: '||lname||' Allergies ');
FOR i in 1 .. total
LOOP DBMS_OUTPUT.PUT_LINE(allergies(i));
end loop;
end;
END;
CREATE OR REPLACE TYPE allergy_ty AS VARRAY(10)
OF VARCHAR2(30);
I don't know how get VARRAY type to print using stored procedure do ya'll lend me some help? -Thanks
You didn't post how tables you use look like, so I'm going to guess. Moreover, I'll use Scott's EMP and DEPT tables to simulate what you have; I'll display department (which is your "patient") and employees who work in that department (which is "allergies" patients suffer from).
SQL> -- this table simulates your PATIENT table
SQL> select * From dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> -- this table simulates your ALLERGIES table
SQL> select deptno, ename from emp;
DEPTNO ENAME
---------- ----------
20 SMITH
30 ALLEN
30 WARD
20 JONES
30 MARTIN
30 BLAKE
10 CLARK
20 SCOTT
10 KING
30 TURNER
20 ADAMS
30 JAMES
20 FORD
10 MILLER
14 rows selected.
SQL>
This is more or less code you wrote; note the way I selected values into p_allergies parameter - not with a join, but a separate query:
SQL> CREATE OR REPLACE TYPE allergy_ty AS VARRAY(10) OF VARCHAR2(30);
2 /
Type created.
SQL> create or replace procedure showallergies
2 (p_pid in number,
3 p_f_name out varchar2,
4 p_l_name out varchar2,
5 p_allergies out allergy_ty
6 )
7 is
8 begin
9 select d.dname, d.loc
10 into p_f_name, p_l_name
11 from dept d
12 where d.deptno = p_pid;
13
14 select e.ename
15 bulk collect into p_allergies
16 from emp e
17 where e.deptno = p_pid;
18 end;
19 /
Procedure created.
SQL>
Let's see how it works:
SQL> set serveroutput on;
SQL> declare
2 fname varchar2(20);
3 lname varchar2(20);
4 allergies allergy_ty;
5 begin
6 showallergies(10, fname, lname, allergies);
7 dbms_output.put_line(fname ||', '|| lname);
8
9 for i in 1 .. allergies.count loop
10 dbms_output.put_line(allergies(i));
11 end loop;
12 end;
13 /
ACCOUNTING, NEW YORK
CLARK
KING
MILLER
PL/SQL procedure successfully completed.
SQL>
Works OK. Now, try to do the same with your tables.
I wanted to if it is possible to detect changes in Oracle DB table for each column and its value and capture the change is a separate temporary table ?
Yes; people usually do that using triggers.
Here's a simple example:
SQL> create table dept_log
2 (deptno number,
3 dname varchar2(20),
4 loc varchar2(20),
5 when date
6 );
Table created.
SQL>
SQL> create or replace trigger trg_bu_dept
2 before update on dept
3 for each row
4 begin
5 if :new.dname <> :old.dname or
6 :new.loc <> :old.loc
7 then
8 insert into dept_log (deptno, dname, loc, when)
9 values (:new.deptno, :old.dname, :old.loc, sysdate);
10 end if;
11 end;
12 /
Trigger created.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> update dept set loc = 'LONDON' where deptno = 40;
1 row updated.
SQL> select * From dept_log;
DEPTNO DNAME LOC WHEN
---------- -------------------- -------------------- -------------------
40 OPERATIONS BOSTON 11.04.2018 22:00:23
SQL>
im trying to do something like this:
create function getData(r1 IN TABLE1%ROWTYPE,col1 string, valor OUT string)RETURN string AS
instruccion VARCHAR2(500);
valor VARCHAR(200);
BEGIN
valor := r1.col1;
return valor;
END getData;
A procedure that takes as parameters a record and the name of the column and give back the value and:
CREATE procedure p1 as
vData VARCHAR2(80);
v1 VARCHAR2(80);
vValue VARCHAR2(80);
inst VARCHAR2(500);
CURSOR vTable2 IS SELECT * FROM TABLE2;
CURSOR vTable1 IS SELECT * FROM TABLE1;
BEGIN
--A cursor for the table with the data
FOR d1 IN vTable1
LOOP
--A cursor for the table that store the name of the columns of the table 1
FOR v1 IN vTable2
LOOP
--get the table1 column name
vData := v1.table2Col1;
--calls the procedure that gives back the value of that record on the column name is stored in VData
inst := 'begin getData(:d1, :vData, :vValue ); end;';
EXECUTE IMMEDIATE inst USING in d1 , vData, OUT vValue;
DBMS_OUTPUT.PUT_LINE(vValue);
END LOOP;
END LOOP;
END p1;
So in summary i just want to have a table that store the names of the table1 in table2, because I want the procedure to work with any table with the name table1, this code gives me the error "expression have to be of SQL types" for the parameters of the dynamic block and i understand it but can figure out a new way to do it. I have tried a lot of things but I'm kind of new in oracle and I really would appreciated the help.
inst := 'begin getData(:d1, :vData, :vValue ); end;';
EXECUTE IMMEDIATE inst USING in d1 , vData, OUT vValue;
I don't see any reason you need to use dynamic SQL here. All you want is to to pass %ROWTYPE parameter and process it. %ROWTYPE is treated as a RECORD, which you would be able to use in the PL/SQL program.
For example,
SQL> CREATE OR REPLACE
2 FUNCTION func(
3 i_dept IN NUMBER,
4 i_emp_rec IN emp%rowtype )
5 RETURN VARCHAR2
6 IS
7 BEGIN
8 RETURN 'DEPTNO = ' || i_dept || ' ENAME = ' || i_emp_rec.ename;
9 END;
10 /
Function created.
SQL>
SQL> SET serveroutput ON
SQL> DECLARE
2 v_deptno dept.deptno%type;
3 cur sys_refcursor;
4 v_ecur sys_refcursor;
5 v_emp emp%rowtype;
6 BEGIN
7 OPEN cur FOR SELECT d.deptno,
8 CURSOR
9 ( SELECT e.* FROM emp e WHERE e.deptno = d.deptno
10 ) e FROM dept d;
11 LOOP
12 FETCH cur INTO v_deptno, v_ecur;
13 EXIT
14 WHEN cur%notfound;
15 LOOP
16 FETCH v_ecur INTO v_emp;
17 EXIT
18 WHEN v_ecur%notfound;
19 dbms_output.put_line(func(v_deptno,v_emp));
20 END LOOP;
21 CLOSE v_ecur;
22 END LOOP;
23 CLOSE cur;
24 END;
25 /
DEPTNO = 10 ENAME = CLARK
DEPTNO = 10 ENAME = KING
DEPTNO = 10 ENAME = MILLER
DEPTNO = 20 ENAME = SMITH
DEPTNO = 20 ENAME = JONES
DEPTNO = 20 ENAME = SCOTT
DEPTNO = 20 ENAME = ADAMS
DEPTNO = 20 ENAME = FORD
DEPTNO = 30 ENAME = ALLEN
DEPTNO = 30 ENAME = WARD
DEPTNO = 30 ENAME = MARTIN
DEPTNO = 30 ENAME = BLAKE
DEPTNO = 30 ENAME = TURNER
DEPTNO = 30 ENAME = JAMES
PL/SQL procedure successfully completed.
SQL>