Errors: FUNCTION EMP_SAL_RANKING - oracle

i dont know why this function is not working ,i actually want to make a function which calculate the salary ranking of the employee based on the current minimum and maximum salaries for employees in the same job category
CREATE OR REPLACE FUNCTION emp_sal_ranking
(empid NUMBER)
RETURN NUMBER IS
minsal emp.sal%type;
maxsal emp.sal%type;
v_jobid emp.job%type;
v_sal emp.sal%type;
BEGIN
select job,sal,INTO v_jobid,V_sal from emp
WHERE empno = empid;
select MIN(sal),MAX(sal) INTO minsal,maxsal
from emp
where job=V_jobid;
return ((V_sal - minsal)/(maxsal-minsal));
END emp_sal_ranking;

"Not working" is difficult to debug. What does it mean?
If it is yet another of your typos (really, you should pay more attention to what you're doing), then remove superfluous comma. After that, function returns result. I don't know whether it is what you wanted or not, but - it "works".
SQL> CREATE OR REPLACE FUNCTION emp_sal_ranking(
2 empid NUMBER
3 )RETURN NUMBER IS
4
5 minsal emp.sal%TYPE;
6 maxsal emp.sal%TYPE;
7 v_jobid emp.job%TYPE;
8 v_sal emp.sal%TYPE;
9 BEGIN
10 SELECT job,
11 sal --> remove comma
12 into v_jobid,
13 v_sal
14 FROM emp
15 WHERE empno = empid;
16
17 SELECT MIN(sal),
18 MAX(sal)
19 INTO
20 minsal,
21 maxsal
22 FROM emp
23 WHERE job = v_jobid;
24
25 return((v_sal - minsal)/(maxsal - minsal));
26 END emp_sal_ranking;
27 /
Function created.
SQL> select emp_sal_ranking(7499) from dual;
EMP_SAL_RANKING(7499)
---------------------
1
SQL>

Related

Statement Select * From Table Where Cursor - How To Loop This Cursor?

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>

How to use outputs from a function?

I have a function with three outputs and one input. But how do I use the value that comes from the outputs? I need to check the values ​​coming out of the outputs. Can someone explain to me how I do it?
As Koen has already said, you don't want to use a function in this case - use a procedure instead. Here's an example: procedure accepts empno and returns their name and salary via OUT parameters.
SQL> create or replace procedure p_test (par_empno in number, par_ename out varchar2, par_sal out number)
2 is
3 begin
4 select ename, sal
5 into par_ename, par_sal
6 from emp
7 where empno = par_empno;
8 end;
9 /
Procedure created.
This is one employee; use its empno when calling the procedure:
SQL> select empno, ename, sal from emp where rownum = 1;
EMPNO ENAME SAL
---------- ---------- ----------
7369 SMITH 920
SQL> set serveroutput on
To call the procedure, declare local variables (because you have to put the result into something):
SQL> declare
2 l_ename emp.ename%type;
3 l_sal emp.sal%type;
4 begin
5 p_test(7369, l_ename, l_sal);
6 dbms_output.put_line(l_ename ||' earns ' || l_sal);
7 end;
8 /
SMITH earns 920
PL/SQL procedure successfully completed.
SQL>
If it must be a function, code changes slightly as function has to return a value anyway (not just via OUT parameters); I chose to return deptno value:
SQL> create or replace function f_test (par_empno in number, par_ename out varchar2, par_sal out number)
2 return varchar2
3 is
4 l_deptno emp.deptno%type;
5 begin
6 select ename, sal, deptno
7 into par_ename, par_sal, l_deptno
8 from emp
9 where empno = par_empno;
10
11 return l_deptno;
12 end;
13 /
Function created.
SQL> declare
2 l_ename emp.ename%type;
3 l_sal emp.sal%type;
4 l_deptno emp.deptno%type;
5 begin
6 l_deptno := f_test(7369, l_ename, l_sal);
7 dbms_output.put_line(l_ename ||' earns ' || l_sal || ' and works in department ' || l_deptno);
8 end;
9 /
SMITH earns 920 and works in department 20
PL/SQL procedure successfully completed.
SQL>

don;t know why code is not working oracle live sql ,getting errors like ORA-06512: at "SYS.DBMS_SQL", line 1721

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).

How to fix ORA-01422:- Fetch isnt working

I'm not understanding why this error is occurring. I have seen examples with him but the people doesn't use the for or the fetch loop. As I have understood, the fetch would loop in all the output of the cursor, in this case, taking row by row of the query and avoiding this error.
See the code:
CREATE OR REPLACE TYPE tab_dias IS TABLE OF INTEGER;
CREATE OR REPLACE FUNCTION uf_dias_internado_paciente
(v_cod_paciente IN internacao.COD_PACIENTE%TYPE,
v_dt_inicio IN internacao.DT_HORA_ENTRADA %TYPE,
v_dt_fim IN internacao.DT_HORA_ALTA %TYPE)
RETURN tab_dias
IS
v_dias tab_dias := tab_dias();
CURSOR c_dias IS
SELECT EXTRACT(DAY FROM (i.DT_HORA_ALTA - i.DT_HORA_ENTRADA)) AS dias
FROM INTERNACAO i
WHERE i.COD_PACIENTE = v_cod_paciente
AND i.DT_HORA_ENTRADA >= v_dt_inicio --13
AND i.DT_HORA_ALTA <= v_dt_fim;
linha_dias c_dias%ROWTYPE;
BEGIN
OPEN c_dias;
LOOP
FETCH c_dias INTO linha_dias;
EXIT WHEN c_dias%NOTFOUND;
v_dias(v_dias.last) := linha_dias.dias;
END LOOP;
CLOSE c_dias;
RETURN v_dias;
END;
The error: ORA-01422: exact fetch returns more than requested number of rows
The code I'm running:
SELECT uf_dias_internado_paciente (5007, CURRENT_TIMESTAMP - 500000, CURRENT_TIMESTAMP ) FROM DUAL;
The desired output:
SELECT (EXTRACT(DAY FROM (i.DT_HORA_ALTA - i.DT_HORA_ENTRADA)))
FROM INTERNACAO i
WHERE i.COD_PACIENTE = 5007
AND i.DT_HORA_ENTRADA >= CURRENT_TIMESTAMP - 500000
AND i.DT_HORA_ALTA <= CURRENT_TIMESTAMP;
(EXTRACT(DAYFROM(I.DT_HORA_ALTA-I.DT_HORA_ENTRADA)))|
----------------------------------------------------|
11|
1|
1|
Oracle documentation:
https://docs.oracle.com/cd/B14117_01/appdev.101/b10807/06_ora.htm#i36655
https://docs.oracle.com/cd/B14117_01/appdev.101/b10807/13_elems020.htm
Code - as you wrote it - will return
ORA-06502: PL/SQL: numeric or value error: NULL index table key value
because you're missing v_dias.EXTEND. It can't fail with ORA-01422 (which is TOO_MANY_ROWS); cursors don't return it (unless you have a subquery - which you don't).
Therefore, there's something wrong in code you posted vs. what you are saying.
As I don't have your tables, here's an example based on Scott's EMP table:
SQL> CREATE OR REPLACE TYPE tab_dias IS TABLE OF INTEGER;
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION f_dias (par_deptno IN NUMBER)
2 RETURN tab_dias
3 IS
4 v_dias tab_dias := tab_dias ();
5
6 CURSOR c_dias IS
7 SELECT empno AS dias
8 FROM emp
9 WHERE deptno = par_deptno;
10
11 linha_dias c_dias%ROWTYPE;
12 BEGIN
13 OPEN c_dias;
14
15 LOOP
16 FETCH c_dias INTO linha_dias;
17
18 EXIT WHEN c_dias%NOTFOUND;
19 v_dias.EXTEND; --> this
20 v_dias (v_dias.LAST) := linha_dias.dias;
21 END LOOP;
22
23 CLOSE c_dias;
24
25 RETURN v_dias;
26 END;
27 /
Function created.
Testing:
SQL> SELECT f_dias (10) FROM DUAL;
F_DIAS(10)
--------------------------------------------------------------------------------
TAB_DIAS(7782, 7839, 7934)
Or:
SQL> SELECT * FROM TABLE(f_dias (10));
COLUMN_VALUE
------------
7782
7839
7934
SQL>
Instead of a loop, perhaps you should consider bulk collect; it is simpler and more efficient:
SQL> CREATE OR REPLACE FUNCTION f_dias (par_deptno IN NUMBER)
2 RETURN tab_dias
3 IS
4 v_dias tab_dias := tab_dias ();
5
6 CURSOR c_dias IS
7 SELECT empno AS dias
8 FROM emp
9 WHERE deptno = par_deptno;
10 BEGIN
11 OPEN c_dias;
12
13 FETCH c_dias BULK COLLECT INTO v_dias;
14
15 CLOSE c_dias;
16
17 RETURN v_dias;
18 END;
19 /
Function created.
SQL> SELECT * FROM TABLE (f_dias (10));
COLUMN_VALUE
------------
7782
7839
7934
SQL>
So, basically, it works. There's something else that produces TOO_MANY_ROWS, not code you posted.

Oracle get id of updated record in trigger update

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>

Resources