How to fix ORA-01422:- Fetch isnt working - oracle

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.

Related

I want to fetch column values from column name rollnoofstud of tableA using PL/SQL

DECLARE
TYPE norollno IS TABLE OF VARCHAR2(100);
rollno norollno;
BEGIN
BEGIN
SELECT token
BULK COLLECT INTO rollno
FROM tableA
WHERE columname='rollnoofstud';
EXCEPTION
WHEN NO_DATA_FOUND THEN
rollno := norollno();
END ;
IF rollno >0 THEN
FOR i IN rollno.FIRST..norollno.LAST
LOOP
<doSomeThing>
END LOOP;
END IF;
END;
I am trying this but I am not getting output. I doubt if my select statement is correct.
I don't have your table so I created one:
SQL> CREATE TABLE tablea
2 AS
3 SELECT ename AS token, 'rollnoofstud' AS columname
4 FROM emp
5 WHERE deptno = 10;
Table created.
Code you posted isn't that wrong; requires a little bit of fixing (see line #17, the way you check whether collection contains something (count it!); typo in FOR loop):
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 TYPE norollno IS TABLE OF VARCHAR2 (100);
3
4 rollno norollno;
5 BEGIN
6 BEGIN
7 SELECT token
8 BULK COLLECT INTO rollno
9 FROM tableA
10 WHERE columname = 'rollnoofstud';
11 EXCEPTION
12 WHEN NO_DATA_FOUND
13 THEN
14 rollno := norollno ();
15 END;
16
17 IF rollno.COUNT > 0
18 THEN
19 FOR i IN rollno.FIRST .. rollno.LAST
20 LOOP
21 DBMS_OUTPUT.put_line (rollno (i));
22 END LOOP;
23 END IF;
24 END;
25 /
CLARK --> here's the result
KING
MILLER
PL/SQL procedure successfully completed.
SQL>
[EDIT: with your sample table and data:]
(note that there's no text datatype in Oracle!)
SQL> CREATE TABLE students
2 (
3 rollnostud INTEGER PRIMARY KEY,
4 name VARCHAR2 (10) NOT NULL,
5 gender VARCHAR2 (1) NOT NULL
6 );
Table created.
SQL> INSERT INTO students
2 VALUES (1, 'Ryan', 'M');
1 row created.
SQL> INSERT INTO students
2 VALUES (2, 'Joanna', 'F');
1 row created.
SQL> INSERT INTO students
2 VALUES (3, 'John', 'M');
1 row created.
Procedure:
SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
2 TYPE norollno IS TABLE OF VARCHAR2 (100);
3
4 rollno norollno;
5 BEGIN
6 BEGIN
7 SELECT name
8 BULK COLLECT INTO rollno
9 FROM students;
10 EXCEPTION
11 WHEN NO_DATA_FOUND
12 THEN
13 rollno := norollno ();
14 END;
15
16 IF rollno.COUNT > 0
17 THEN
18 FOR i IN rollno.FIRST .. rollno.LAST
19 LOOP
20 DBMS_OUTPUT.put_line (rollno (i));
21 END LOOP;
22 END IF;
23 END;
24 /
Ryan
Joanna
John
PL/SQL procedure successfully completed.
SQL>

Need to fetch the table details using stored procedure when we give table name as input

CREATE TABLE test_table
(
col1 NUMBER(10),
col2 NUMBER(10)
);
INSERT INTO test_table
VALUES (1, 2);
I am writing a stored procedure wherein if I give a table name as an input, that should give me the table data and column details.
For example:
SELECT *
FROM <input_table_name>;
But this causes an error that the SQL command has not ended properly even though I have taken care of this.
My attempt:
CREATE OR REPLACE PROCEDURE sp_test(iv_table_name IN VARCHAR2,
p_out_cur OUT SYS_REFCURSOR)
AS
lv_str VARCHAR2(400);
lv_count NUMBER(1);
lv_table_name VARCHAR2(255):=UPPER(iv_table_name);
BEGIN
lv_str := 'SELECT * FROM '||lv_table_name;
SELECT COUNT(1) INTO lv_count FROM all_tables WHERE table_name = lv_table_name;
IF lv_count = 0 THEN
dbms_output.put_line('Table does not exist');
ELSE
OPEN p_out_cur FOR lv_str;
END IF;
END sp_test;
Tool used: SQL developer(18c)
In dynamic SQL, you do NOT terminate statement with a semicolon.
EXECUTE IMMEDIATE 'SELECT * FROM '||lv_table_name||';';
-----
remove this
Anyway, you won't get any result when you run that piece of code. If you really want to see table's contents, you'll have to switch to something else, e.g. create a function that returns ref cursor.
Sample data:
SQL> SELECT * FROM test_table;
COL1 COL2
---------- ----------
1 2
3 4
Procedure you wrote is now correct:
SQL> CREATE OR REPLACE PROCEDURE sp_test (iv_table_name IN VARCHAR2,
2 p_out_cur OUT SYS_REFCURSOR)
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 BEGIN
8 lv_str := 'SELECT * FROM ' || lv_table_name;
9
10 SELECT COUNT (1)
11 INTO lv_count
12 FROM all_tables
13 WHERE table_name = lv_table_name;
14
15 IF lv_count = 0
16 THEN
17 DBMS_OUTPUT.put_line ('Table does not exist');
18 ELSE
19 OPEN p_out_cur FOR lv_str;
20 END IF;
21 END sp_test;
22 /
Procedure created.
Testing:
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_rc SYS_REFCURSOR;
3 l_col1 NUMBER (10);
4 l_col2 NUMBER (10);
5 BEGIN
6 sp_test ('TEST_TABLE', l_rc);
7
8 LOOP
9 FETCH l_rc INTO l_col1, l_col2;
10
11 EXIT WHEN l_rc%NOTFOUND;
12
13 DBMS_OUTPUT.put_line (l_col1 || ', ' || l_col2);
14 END LOOP;
15 END;
16 /
1, 2 --> contents of the
3, 4 --> TEST_TABLE
PL/SQL procedure successfully completed.
SQL>
A function (instead of a procedure with the OUT parameter):
SQL> CREATE OR REPLACE FUNCTION sf_test (iv_table_name IN VARCHAR2)
2 RETURN SYS_REFCURSOR
3 AS
4 lv_str VARCHAR2 (400);
5 lv_count NUMBER (1);
6 lv_table_name VARCHAR2 (255) := UPPER (iv_table_name);
7 l_rc SYS_REFCURSOR;
8 BEGIN
9 lv_str := 'SELECT * FROM ' || lv_table_name;
10
11 SELECT COUNT (1)
12 INTO lv_count
13 FROM all_tables
14 WHERE table_name = lv_table_name;
15
16 IF lv_count = 0
17 THEN
18 raise_application_error (-20000, 'Table does not exist');
19 ELSE
20 OPEN l_rc FOR lv_str;
21 END IF;
22
23 RETURN l_rc;
24 END sf_test;
25 /
Function created.
Testing:
SQL> SELECT sf_test ('liksajfla') FROM DUAL;
SELECT sf_test ('liksajfla') FROM DUAL
*
ERROR at line 1:
ORA-20000: Table does not exist
ORA-06512: at "SCOTT.SF_TEST", line 18
SQL> SELECT sf_test ('TEST_TABLE') FROM DUAL;
SF_TEST('TEST_TABLE'
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
COL1 COL2
---------- ----------
1 2
3 4
SQL>

how to join cursor returned from procedure with other tables in oracle

i have a procedure that give me a refcursor as out parameter and i have to join that data with other tables in my own package.
i try to fetch cursor in a variable of the same return type but get: PLS-00497: cannot mix between single row and multi-row (BULK) in INTO list
types defined in external package (i cannot alter it)
CURSOR CUR_MYTYPE IS SELECT f1... FROM xxx
TYPE MYTYPE IS REF_CURSOR RETURN CUR_MYTYPE%ROWTYPE
my own code:
result SCHEMA.MYTYPE;
cur sys_refcursor;
check VARCHAR2;
PKG.PROCEDURE_NAME(check,cur);
fetch cur bulk collect into result ;
select t.f1, t.f2, t.f3, o.f1, o.f2
from table(result ) t
inner join otherTable o
on o.f1 = t.f1
Are you sure you've got all that correct? You cannot bulk collect into "result" if result is defined as a refcursor, eg
SQL> declare
2 cursor cur_mytype is select * from emp;
3 type mytype is ref cursor return cur_mytype%rowtype;
4 cur sys_refcursor;
5 result mytype;
6 begin
7 open cur for select * from emp;
8 fetch cur bulk collect into result;
9 end;
10 /
fetch cur bulk collect into result;
*
ERROR at line 8:
ORA-06550: line 8, column 31:
PLS-00497: cannot mix between single row and multi-row (BULK) in INTO list
I am going to assume it is more like this:
SQL> declare
2 cursor cur_mytype is select * from emp;
3 type mytype is table of cur_mytype%rowtype index by pls_integer;
4
5 cur sys_refcursor;
6 result mytype;
7 begin
8 open cur for select * from emp;
9 fetch cur bulk collect into result;
10 end;
11 /
PL/SQL procedure successfully completed.
In any event, MYTYPE needs to be a nested table type (which it is not in the case above) so that you can run the TABLE() function around it. If that is not the case, then you could create your own local schema type which maps to the columns and then transfer the rows to that, eg
--
-- incorrect type
--
SQL> declare
2 cursor cur_mytype is select * from emp;
3 type mytype is table of cur_mytype%rowtype index by pls_integer;
4
5 cur sys_refcursor;
6 result mytype;
7 begin
8 open cur for select * from emp;
9 fetch cur bulk collect into result;
10
11 for i in ( select * from table(result) )
12 loop
13 null;
14 end loop;
15 end;
16 /
for i in ( select * from table(result) )
*
ERROR at line 11:
ORA-06550: line 11, column 34:
PLS-00382: expression is of wrong type
ORA-06550: line 11, column 28:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item
ORA-06550: line 11, column 14:
PL/SQL: SQL Statement ignored
--
-- data copied to corrected type
--
SQL> create or replace type emp_row as object (
2 empno number(4)
3 ,ename varchar2(10)
4 ,job varchar2(9)
5 ,mgr number(4)
6 ,hiredate date
7 ,sal number(7,2)
8 ,comm number(7,2)
9 ,deptno number(2)
10 );
11 /
Type created.
SQL>
SQL> create or replace type my_fixed_type as table of emp_row;
2 /
Type created.
SQL> declare
2 cursor cur_mytype is select * from emp;
3 type mytype is table of cur_mytype%rowtype index by pls_integer;
4
5 cur sys_refcursor;
6 result mytype;
7
8 fixed_result my_fixed_type := my_fixed_type();
9
10 begin
11 open cur for select * from emp;
12 fetch cur bulk collect into result;
13
14 for i in 1 .. result.count loop
15 fixed_result.extend;
16 fixed_result(i) :=
17 emp_row(
18 result(i).empno,
19 result(i).ename,
20 result(i).job,
21 result(i).mgr,
22 result(i).hiredate,
23 result(i).sal,
24 result(i).comm,
25 result(i).deptno);
26 end loop;
27
28 for i in ( select * from table(fixed_result) )
29 loop
30 null;
31 end loop;
32 end;
33 /
PL/SQL procedure successfully completed.

Errors: FUNCTION EMP_SAL_RANKING

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>

How to update ref cursor values in oracle?

I want to fetch limited no. of rows using refcursor. then I need to update same set of records. is it possible?
create or replace PROCEDURE myproc (
P_ROWCOUNT IN NUMBER,
OUT_TXN_IDS OUT OF_CR_TYPE,
P_CD_ERROR OUT NUMBER,
P_DS_ERROR OUT VARCHAR2
)
AS
V_TXNID NUMBER;
BEGIN
P_CD_ERROR := 0;
P_DS_ERROR := 'SUCCESS';
OPEN OUT_TXN_IDS for
SELECT id FROM table1 WHERE status='N' AND ROWNUM<=P_ROWCOUNT;
EXCEPTION
WHEN OTHERS THEN
P_CD_ERROR := sqlcode;
P_DS_ERROR := 'WF-ERROR - myproc - ' || substr(SQLERRM, 1, 200);
RETURN;
END myproc;
I need to update same records to status Y after refcursor returns. can we do this. please suggest
I don't have your tables nor data so I simplified it a little bit, but - it should work nonetheless.
Initial statuses:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 7
N 7
Procedure: basically, you'd modify rows represented by ID returned by ref cursor.
SQL> DECLARE
2 out_txn_ids SYS_REFCURSOR;
3 p_rowcount NUMBER := 5;
4 l_id table1.id%TYPE;
5 BEGIN
6 OPEN out_txn_ids FOR SELECT id
7 FROM table1
8 WHERE status = 'N'
9 AND ROWNUM <= p_rowcount;
10
11 LOOP
12 FETCH out_txn_ids INTO l_id;
13
14 EXIT WHEN out_txn_ids%NOTFOUND;
15
16 UPDATE table1
17 SET status = 'Y'
18 WHERE id = l_id;
19 END LOOP;
20
21 CLOSE out_txn_ids;
22 END;
23 /
PL/SQL procedure successfully completed.
Result:
SQL> SELECT status, count(*) FROM table1 group by status;
S COUNT(*)
- ----------
Y 12
N 2
SQL>

Resources