By below code is not working for cursor on PLSQL.
I receive an error message.
declare
type abc is varray(10) of number;
cursor x is select Empno from emp where rownum <10;
a abc;
counter number :=1;
begin
a:=abc();
for i in x
loop
a.extend();
a(i):=counter.Empno;
DBMS_output.put_line(a(i));
counter:= end loop;
end;
You have used counter instead of for loop iterator i:
Try;
declare
type abc is varray(10) of number;
cursor x is select Empno from emp where rownum <10;
a abc;
counter number := 1;
begin
a:=abc();
for i in x loop
a.extend();
a(counter) := i.Empno;
DBMS_output.put_line(a(counter));
counter := counter + 1;
end loop;
end;
Or you can use Bulk collect to insert data into varray
declare
type abc is varray(10) of number;
a abc := abc();
begin
select Empno BULK COLLECT INTO a from emp where rownum <10;
for i in 1 .. a.count loop
DBMS_output.put_line(a(i));
end loop;
end;
Related
I have a table called stud_ans_sheet.
I want to run a loop
which would every time fetch next values from table.
It's a PL/SQL program and I am doing it with a cursor.
This is my program,
but it has a lot of errors:
set serveroutput on;
declare
cursor c_stud is select stud_no,ans from stud_ans_sheet;
v_stud c_stud%rowtype;
v_no stud_ans_sheet.stud_no%type;
answer varchar(10);
i number(3);
v_corr stud_ans_sheet.corr_ans%type;
v_wrong stud_ans_sheet.wrong_ans%type;
v_unattempt stud_ans_sheet.unattempt_ans%type;
score number(5,2);
v_ans varchar(10);
str1 varchar(40);
str2 varchar(40);
nval stud_ans_sheet.stud_no%type;
total number(5,2);
begin
answer:='AACCABAABD';
open c_stud;
loop
fetch c_stud into v_stud;
exit when c_stud%notfound;
for i in 1..10
loop
nval:= select seq.nextval from stud_ans_sheet.stud_no;
select stud_no,ans into v_no,v_ans from stud_ans_sheet where stud_no=nval;
str2:=substr(v_ans,i,1);
str1:=substr(answer,i,1);
if(str2=str1) then
update stud_ans_sheet
set corr_ans=v_corr+1;
elsif(str2='E') then
update stud_ans_sheet
set unattempt_ans=v_unattempt+1;
else
update stud_ans_sheet
set wrong_ans=v_wrong+1;
end if;
end loop;
update stud_ans_sheet
set score=corr_ans-wrong_ans*0.25+unattempt_ans;
end loop;
close c_stud;
end;
/
This line doesn't really make sense:
nval:= select seq.nextval from stud_ans_sheet.stud_no;
There is no variable := select ... construction in PL/SQL, and even if there was, stud_ans_sheet.stud_no isn't a table so you can't select from it, and if you could it's unclear what you want the sequence for anyway.
If you just want to copy the record attribute v_stud.stud_no into the local variable nval then you can just use:
nval := v_stud.stud_no;
but then the question is why copy it at all, when you can just use v_stud.stud_no directly?
It is generally best to avoid the verbose declare-open-fetch construction for cursors when all you need to do is loop through them. Instead, you can just use:
for r in (
select stud_no, ans from stud_ans_sheet
)
loop
I can't see why you are re-querying stud_ans_sheet in the loop just to get back the same row you already have. If it's something to do with your update logic, I couldn't tell what that was supposed to do.
The update statements in your code will update every row in the table every time, because they don't have any where clause.
In PL/SQL, if conditions are terminated by then instead of being enclosed in brackets as in some other languages, so for example,
if (str2 = str1) then
can be decluttered into:
if str2 = str1 then
The construction for i in 1..10 implicitly declares i with its scope as the loop, so the other i you declared at the top is a different variable and not used.
You are allowed to use spaces for readability, so
update stud_ans_sheet
set score=corr_ans-wrong_ans*0.25+unattempt_ans
where ...
can be written more clearly as
update stud_ans_sheet
set score = corr_ans - wrong_ans * 0.25 + unattempt_ans
where ...;
Probably you should focus on one error at a time rather than dumping the whole thing here for review.
I have changed some of your code, try this:
SET SERVEROUTPUT ON;
DECLARE
ANSWER VARCHAR (10);
V_CORR STUD_ANS_SHEET.CORR_ANS%TYPE;
V_WRONG STUD_ANS_SHEET.WRONG_ANS%TYPE;
V_UNATTEMPT STUD_ANS_SHEET.UNATTEMPT_ANS%TYPE;
STR1 VARCHAR (40);
STR2 VARCHAR (40);
BEGIN
ANSWER := 'AACCABAABD';
FOR R_STUD IN (SELECT STUD_NO, ANS
FROM STUD_ANS_SHEET)
LOOP
V_CORR := 0;--It's better to have only one update
V_WRONG := 0;
V_UNATTEMPT := 0;
FOR I IN 1 .. 10
LOOP
STR2 := SUBSTR (R_STUD.ANS, I, 1);
STR1 := SUBSTR (ANSWER, I, 1);
IF (STR2 = STR1)
THEN
V_CORR := V_CORR + 1;
ELSIF (STR2 = 'E')
THEN
V_UNATTEMPT := V_UNATTEMPT + 1;
ELSE
V_WRONG := V_WRONG + 1;
END IF;
END LOOP;
UPDATE STUD_ANS_SHEET
SET SCORE = V_CORR- V_WRONG* 0.25 + V_UNATTEMPT,
CORR_ANS = V_CORR,
WRONG_ANS = V_WRONG,
UNATTEMPT_ANS = V_UNATTEMPT
WHERE R_STUD.STUD_NO = STUD_NO;
END LOOP;
END;
/
SET SERVEROUTPUT ON;
DECLARE
aswer varchar (10);
v_corr number(3);
v_wrong number (3);
v_unattempt number (3);
str1 VARCHAR (10);
str2 VARCHAR (10);
BEGIN
answer:= 'AACCABAABD';
FOR v_stud in c_stud
LOOP
v_corr:= 0;
v_wrong:= 0;
v_unattempt := 0;
FOR I IN 1 .. 10
LOOP
str2 := SUBSTR (v_stud.ans, i, 1);
str1 := SUBSTR (answer, i, 1);
IF (str2 = str1)
THEN
v_corr := v_corr + 1;
ELSIF (str2 = 'E')
THEN
v_unattempt := v_unattempt + 1;
else
v_wrong:= v_wrong + 1;
END IF;
END LOOP;
UPDATE stud_ans_sheet
SET
corr_ans= v_corr,
wrong_ans = v_wrong,
unattempt_ans=v_unattempt , score = v_corr- v_wrong* 0.25,
WHERE stud_no= v_stud.stud_no;
END LOOP;
END;
/
Please help me writing a program procedure with refcursor. Refcursor should be out parameter.
If i give input deptno it shows employee details in that department,also write unit testing block :
I tried like:
CREATE OR REPLACE PROCEDURE poph (i IN NUMBER, p OUT SYS_REFCURSOR)
IS
BEGIN
OPEN p FOR 'select * from emp where deptno=' || i;
FOR i IN p
LOOP
DBMS_OUTPUT.put_line (i.ename || '**' || i.JOB);
END LOOP;
CLOSE p;
END;
/
///////unit testing/////
DECLARE
M NUMBER := '&n';
n SYS_REFCURSOR;
BEGIN
poph (M, n);
END;
You are making 2 mistakes.
1) Openin a refcursor for dynamic select statement which will not work without execute immediate statement.
OPEN p FOR 'select * from emp where deptno=' || i;
2) Looping through a ref cursor which is not valid;
FOR i IN p
Do it this way:
CREATE OR REPLACE PROCEDURE poph (i IN NUMBER, p OUT SYS_REFCURSOR)
IS
v_sql varchar2(100);
BEGIN
v_sql := 'select employee_id,salary from employee where employee_id=' || i;
execute immediate v_sql;
OPEN p FOR v_sql;
END;
/
///////unit testing/////
DECLARE
M NUMBER := '&n';
n SYS_REFCURSOR;
v_emp_id NUMBER;
v_sal NUMBER;
BEGIN
poph (M, n);
LOOP
FETCH n INTO v_emp_id, v_sal;
EXIT WHEN n%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (v_emp_id);
END LOOP;
END;
Well in Oracle, EXECUTE IMMEDIATE is simpler than resorting to the dbms_sql package with the exception of cross tenant queries as dbms_sql as of 12c allows a container argument.
However, in a small test, I'm getting nothing. i.e.,
set serveroutput on
declare
ret pls_integer;
cnt pls_integer := 0;
cols number := 0;
ctx varchar2(128) := NULL;
cur number;
stmt varchar2(100);
begin
stmt := 'select count(*) from scott.emp';
cur := dbms_sql.open_cursor;
dbms_sql.parse(c => cur,
statement => stmt,
language_flag => dbms_sql.native,
container => ctx);
dbms_sql.define_column(cur, 1, cnt);
ret := dbms_sql.execute(cur);
dbms_output.put_line('execute = '||ret);
ret := dbms_sql.fetch_rows(cur);
dbms_output.put_line(' fetch = '||ret);
dbms_output.put_line(stmt||' = '||cnt);
dbms_sql.close_cursor(cur);
end;
/
The container when null - meaning current or ignored would allow the query to sent to the target container. I however suspect I'm missing something trivial, hence my question; thanks in advance. Sample output:
SQL> show con_name
CON_NAME
------------------------------
PDB1
SQL> set echo on
SQL> #f
SQL> set serveroutput on
SQL> declare
2 ret pls_integer;
3 cnt pls_integer := 0;
4 cols number := 0;
5 ctx varchar2(128) := NULL;
6 cur number;
7 stmt varchar2(100);
8 begin
9 stmt := 'select count(*) from scott.emp';
10 cur := dbms_sql.open_cursor;
11 dbms_sql.parse(c => cur,
12 statement => stmt,
13 language_flag => dbms_sql.native,
14 container => ctx);
15 dbms_sql.define_column(cur, 1, cnt);
16 ret := dbms_sql.execute(cur);
17 dbms_output.put_line('execute = '||ret);
18 ret := dbms_sql.fetch_rows(cur);
19 dbms_output.put_line('fetch = '||ret);
20 dbms_output.put_line(stmt||' = '||cnt);
21 dbms_sql.close_cursor(cur);
22 end;
23 /
execute = 0
fetch = 1
select count(*) from scott.emp = 0
PL/SQL procedure successfully completed.
You need to get the actual column value from the result set with:
dbms_sql.column_value(cur, 1, cnt);
From the documentation:
COLUMN_VALUE Procedure
This procedure returns the value of the cursor element for a given position in a given cursor. This procedure is used to access the data fetched by calling FETCH_ROWS.
So your code would be:
set serveroutput on
declare
ret pls_integer;
cnt pls_integer := 0;
cols number := 0;
ctx varchar2(128) := NULL;
cur number;
stmt varchar2(100);
begin
stmt := 'select count(*) from scott.emp';
cur := dbms_sql.open_cursor;
dbms_sql.parse(c => cur,
statement => stmt,
language_flag => dbms_sql.native,
container => ctx);
dbms_sql.define_column(cur, 1, cnt);
ret := dbms_sql.execute(cur);
dbms_output.put_line('execute = '||ret);
ret := dbms_sql.fetch_rows(cur);
dbms_output.put_line(' fetch = '||ret);
-- get the actual value from the column into its variable
dbms_sql.column_value(cur, 1, cnt);
dbms_output.put_line(stmt||' = '||cnt);
dbms_sql.close_cursor(cur);
end;
/
If I do that (admittedly in 11g, so without the container argument - which doesn't seem to really be relevant to your actual issue) and against the hr.employees table instead, I see:
execute = 0
fetch = 1
select count(*) from employees = 107
PL/SQL procedure successfully completed.
SQL> select count(*) from employees;
COUNT(*)
----------
107
I creatd a stored procedure. And a global temporary table inside it.
Now i am inserting data into this table by fatching cursor into local variables.
create or replace PROCEDURE "DEMO"
(
PARM_YEAR IN NUMBER,
PARM_PERIOD IN NUMBER,
PARM_PERIOD_TYPE IN CHAR,
PARM_PERIOD_ROLL IN CHAR,
PARM_TYPE IN VARCHAR2,
P_CURSOR IN OUT types.cursorType
)
IS
LOC_EXISTS NUMBER;
LOC_TYPE_PERIOD CHAR(2);
LOC_CURSOR_YEAR INTEGER;
LOC_CURSOR_PERIOD INTEGER;
LOC_CURSOR_TYPE_PERIOD CHAR(2 BYTE);
LOC_CURSOR_DEV_COPCL NUMBER(10,1);
LOC_CURSOR_DIST_GRP NUMBER(3,0);
CURSOR DIST_CHART IS
SELECT X.YEAR , X.PERIOD, X.TYPE_PERIOD, X.COST AS DEV_COPCL
FROM
SMY_PRVDR_TYPE_PRVDR X;
BEGIN
/* Set Period type */
IF (PARM_PERIOD_TYPE = 'Q' AND PARM_PERIOD_ROLL = 'A') THEN
LOC_TYPE_PERIOD := 'Q';
ELSE IF (PARM_PERIOD_TYPE = 'Q' AND PARM_PERIOD_ROLL = 'R') THEN
LOC_TYPE_PERIOD := 'RQ';
ELSE IF (PARM_PERIOD_TYPE = 'M' AND PARM_PERIOD_ROLL = 'A') THEN
LOC_TYPE_PERIOD := 'M';
ELSE
LOC_TYPE_PERIOD := 'RM';
END IF;
END IF;
END IF;
LOC_EXISTS := 0;
SELECT 1 INTO LOC_EXISTS
FROM ALL_TABLES
WHERE TABLE_NAME LIKE '%DEMO1%';
IF LOC_EXISTS = 1 THEN
EXECUTE IMMEDIATE 'TRUNCATE TABLE DEMO1';
END IF;
OPEN DIST_CHART;
LOOP
FETCH DIST_CHART INTO LOC_CURSOR_YEAR, LOC_CURSOR_PERIOD, LOC_CURSOR_TYPE_PERIOD,
LOC_CURSOR_DEV_COPCL;
EXIT WHEN DIST_CHART%NOTFOUND;
SELECT DIST_GRP(LOC_CURSOR_DEV_COPCL) INTO LOC_CURSOR_DIST_GRP FROM DUAL;
EXECUTE IMMEDIATE 'INSERT INTO DEMO1 VALUES ('|| LOC_CURSOR_YEAR ||', '''||
LOC_CURSOR_PERIOD || ''', '''|| LOC_CURSOR_TYPE_PERIOD ||''', '|| LOC_CURSOR_DEV_COPCL
||', '|| LOC_CURSOR_DIST_GRP || ')';
END LOOP;
CLOSE DIST_CHART;
EXCEPTION
WHEN NO_DATA_FOUND THEN
LOC_EXISTS:=0;
END;
Problem is that..
When i am executing this stored procedure into sql developer data is inserted successfuly into the global temporary table.
But
When i run same execute command into SQLPLUS, procedure run successfully, but data is not inserted into the global temporary table.
Code of GTT is
CREATE GLOBAL TEMPORARY TABLE "ICUSER"."DEMO1"
( "YEAR" NUMBER(4,0),
"PERIOD" NUMBER(2,0),
"TYPE_PERIOD" CHAR(2 BYTE),
"DEV_COPCL" NUMBER(*,1),
"DIST_GRP" NUMBER(3,0)
) ON COMMIT PRESERVE ROWS ;
I have a function which concatinates the value for a cursor. Its now only concatinates 4 columns and that column name should be hardcoded. Is there a way to have a generic solution for this, such that if i pass a cursor it will automatically concatinate data regardless of column name and number of columns in 11g.
FUNCTION generateData(p_dataCursor IN SYS_REFCURSOR)
RETURN VARCHAR2 AS
-- ---------------------------------------------------------------------
crlf VARCHAR2(2) := chr(13)||chr(10);
lv_message VARCHAR2(32000);
BEGIN
FOR rec IN p_dataCursor
LOOP
lv_message := lv_message || rec.a||','||rec.b||','||rec.c||','||rec.d || crlf;
END LOOP;
RETURN lv_message;
END;
Since 11g Oracle built-in package DBMS_SQL provides function TO_CURSOR_NUMBER - "This function takes an OPENed strongly or weakly-typed ref cursor and transforms it into a DBMS_SQL cursor number."
Example code:
DECLARE
l_cursor SYS_REFCURSOR;
FUNCTION generateData(p_dataCursor IN SYS_REFCURSOR)
RETURN VARCHAR2 AS
curs SYS_REFCURSOR := p_dataCursor;
l_cursorid NUMBER;
l_column_count INTEGER;
l_describe_table DBMS_SQL.DESC_TAB;
l_numvar NUMBER;
l_ignore INTEGER;
l_value VARCHAR2(2000);
l_coma VARCHAR2(10);
crlf VARCHAR2(2) := chr(13)||chr(10);
lv_message VARCHAR2(32000);
BEGIN
l_cursorid := dbms_sql.to_cursor_number( curs );
dbms_sql.describe_columns( l_cursorid, l_column_count, l_describe_table );
FOR i IN 1..l_column_count LOOP
dbms_sql.define_column(l_cursorid, i, l_value, 2000);
END LOOP;
LOOP
IF DBMS_SQL.FETCH_ROWS(l_cursorid)>0 THEN
l_coma := '';
FOR i IN 1..l_column_count LOOP
dbms_sql.column_value(l_cursorid, i, l_value);
lv_message := lv_message || l_coma || l_value;
l_coma := ',';
END LOOP;
lv_message := lv_message || crlf;
ELSE
EXIT;
END IF;
END LOOP;
dbms_sql.close_cursor( l_cursorid );
RETURN lv_message;
END;
BEGIN
open l_cursor FOR 'SELECT 1 as A, 2 AS B, 3 AS C, 4 AS D FROM DUAL UNION ALL SELECT 1 as A, 2 AS B, 3 AS C, 4 AS D FROM DUAL';
dbms_Output.put_Line(generateData(l_cursor));
END;
/