How to resolve 'ORA-01002: fetch out of sequence' error? - oracle

I am facing 'ORA-01002: fetch out of sequence' error as a value for 'P_RESPONSE' cursor. It doesn't matter if select query returns any records, error is there in the output every single time.
Please note that 'REQUESTSUBMITTERS.USERNAME' is a primary key.
PFB Stored Procedure.
create or replace PROCEDURE GET_REQUESTSUBMITTER (
P_USERNAME IN REQUESTSUBMITTERS.USERNAME%TYPE,
P_RESPONSE OUT SYS_REFCURSOR,
SPRESULT OUT VARCHAR2,
SPRESPONSECODE OUT VARCHAR2,
SPRESPONSEMESSAGE OUT VARCHAR2
)
AS
L_REQUESTSUBMITTER REQUESTSUBMITTERS%ROWTYPE;
BEGIN
OPEN P_RESPONSE FOR
SELECT USERNAME, GEN, FIRSTNAME, LASTNAME, EMAILID, OFFICELOCATION, TITLE, MANAGER, DEPARTMENT
FROM REQUESTSUBMITTERS
WHERE UPPER(USERNAME)=UPPER(P_USERNAME);
LOOP
EXIT WHEN P_RESPONSE%NOTFOUND;
FETCH P_RESPONSE INTO L_REQUESTSUBMITTER;
END LOOP;
IF P_RESPONSE%ROWCOUNT=0 THEN
OPEN P_RESPONSE FOR
SELECT 'NA' AS USERNAME FROM DUAL;
SPRESULT:='NOK';
SPRESPONSECODE:='GETREQSUBMTR-002';
SPRESPONSEMESSAGE:=CONCAT('SUBMITTER RECORD NOT FOUND FOR USER - ', P_USERNAME);
ELSE
SPRESULT:='OK';
SPRESPONSECODE:='GETREQSUBMTR-001';
SPRESPONSEMESSAGE:=CONCAT('SUBMITTER RECORD FOUND FOR USER - ', P_USERNAME);
END IF;
END GET_REQUESTSUBMITTER;

I believe your EXIT statement should be after the FETCH statement inside the loop, else you'll loop past the end.
Don't forget to CLOSE the cursor.

Do the following changes and see if your procedure works
create or replace PROCEDURE GET_REQUESTSUBMITTER (
P_USERNAME IN REQUESTSUBMITTERS.USERNAME%TYPE,
P_RESPONSE OUT SYS_REFCURSOR,
SPRESULT OUT VARCHAR2,
SPRESPONSECODE OUT VARCHAR2,
SPRESPONSEMESSAGE OUT VARCHAR2
)
AS
L_REQUESTSUBMITTER REQUESTSUBMITTERS%ROWTYPE;
L_ROW_CNT NUMBER;
BEGIN
OPEN P_RESPONSE FOR
SELECT USERNAME, GEN, FIRSTNAME, LASTNAME, EMAILID, OFFICELOCATION, TITLE, MANAGER, DEPARTMENT
FROM REQUESTSUBMITTERS
WHERE UPPER(USERNAME)=UPPER(P_USERNAME);
LOOP
FETCH P_RESPONSE INTO L_REQUESTSUBMITTER;
EXIT WHEN P_RESPONSE%NOTFOUND;
END LOOP;
L_ROW_CNT:= P_RESPONSE%ROWCOUNT;
CLOSE P_RESPONSE;
IF L_ROW_CNT=0 THEN
OPEN P_RESPONSE FOR
SELECT 'NA' AS USERNAME FROM DUAL;
SPRESULT:='NOK';
SPRESPONSECODE:='GETREQSUBMTR-002';
SPRESPONSEMESSAGE:=CONCAT('SUBMITTER RECORD NOT FOUND FOR USER - ', P_USERNAME);
CLOSE P_RESPONSE;
ELSE
SPRESULT:='OK';
SPRESPONSECODE:='GETREQSUBMTR-001';
SPRESPONSEMESSAGE:=CONCAT('SUBMITTER RECORD FOUND FOR USER - ', P_USERNAME);
END IF;
END GET_REQUESTSUBMITTER;

Related

oracle explicit cursor loop

I want to replace the domain name using cursor without a loop, which is an explicit cursor. but I also want to changes all the same domain names in the database by given pass a string.
example: exec PR_Q3( 'usa.com','hotmail.com'); all the domain name with 'usa.com'in database will change to 'hotmail.com'.
create or replace procedure PR_Q3
(old_email in varchar2, new_email in varchar2)
authid current_user
is
cursor E_info is select Email_Address from broker
where REGEXP_LIKE (substr(Email_Address, instr(Email_Address,'#')+1), old_email);
v_email E_info%rowtype;
begin
open E_info;
loop
fetch E_info into v_email;
exit when E_info%notfound;
update broker set
Email_Address = replace(Email_Address,substr(Email_Address,instr(Email_Address,'#')+1),new_email)
where Email_Address = v_email.Email_Address;
end loop;
close E_info;
end PR_Q3;
it works if I delete the loop, but it only changes the domain name once.
I need changes all the same domain name.
I want to do the same thing without a loop. Can I?
You can use the following simple update in your procedure:
CREATE OR REPLACE PROCEDURE PR_Q3 (
OLD_EMAIL IN VARCHAR2,
NEW_EMAIL IN VARCHAR2
)
AUTHID CURRENT_USER
IS
BEGIN
UPDATE BROKER
SET
EMAIL_ADDRESS = REPLACE(EMAIL_ADDRESS, OLD_EMAIL, NEW_EMAIL)
WHERE
REGEXP_LIKE ( EMAIL_ADDRESS,
'.*#' || OLD_EMAIL || '$' );
COMMIT;
END PR_Q3;
/
Cheers!!

Validation of result before executing the query

When running a stored procedure to fetch some rows, First I want to validate if the query will return a row before sending the result, and second if it is possible to validate without running the same query twice.
I am using a cursor to store the yielded result, So I tried the cursor attribute %ROWCOUNT & %NOTFOUND. But the doesnt quite work. Plus I want to do this without running a loop on the cursor.
procedure MODULE_LIST_GK(p_module_Id IN MODULE_LIST.MODULE_ID% TYPE,
p_Error_Code out nvarchar2,
p_Error_Msg out nvarchar2,
p_Cursor out sys_refcursor) IS
BEGIN
OPEN p_Cursor FOR
SELECT A.MODULE_ID,
A.MODULE_NM,
A.AUTH_STATUS_ID
FROM MODULE_LIST A
WHERE A.MODULE_ID=p_module_Id;
SELECT COUNT(MODULE_ID)
INTO v_row_num
FROM MODULE_LIST A
WHERE A.MODULE_ID=p_module_Id;
IF v_row_num=0 THEN
p_Error_Code := SQLCODE;
p_Error_Msg := 'Does not Exists';
Return;
end IF;
EXCEPTION
WHEN OTHERS THEN
p_error_code:= SQLCODE;
p_error_msg := SQLERRM;
END MODULE_LIST_GK;
Your implementation have several points that could be improved.
First if you expect that for lot of parameters the returned cursor will be empty,
than first check the empty cursor and only after this check open the cursor. You do it vice versa.
How to check if the cursor is empty? Unfortunatelly you must fetch the first row to be able to verify it.
open l_cur for
select id, status from tab where id = p_id;
fetch l_cur into l_id, l_status;
if l_cur%NOTFOUND then
p_Error_Msg := 'Does not Exists';
Return;
end if;
This check is far more effective that the often used count(*) as it is considering only the first (few) rows and not counting all rows in the cursor.
If the check fails you are ready, othervise simple open the cursor and return it.
open l_cur for
select id, status from tab where id = p_id;
p_Cursor := l_cur;
Two additional thinks come to mind.
You should rething the generall approach if the database is very dynamic. How would you handle the case when other session deletes some row between the check and the second open of the cursor?
Finally consider returning an exception instead of the return code.
In order to know whether a cursor contains rows, you must open it and fetch the first row. Once you've done this, it makes no sense anymore to return that cursor, for the recipient will not be able to fetch that first row, because the cursor already points beyond it.
So, you must select twice. What you'd do is to use ROWNUM or an EXISTS clause here to show the DBMS that you are not interested in any more rows. This can speed up the query extremely.
PROCEDURE module_list_gk(p_module_id IN MODULE_LIST.MODULE_ID%TYPE,
p_error_code OUT NVARCHAR2,
p_error_msg OUT NVARCHAR2,
p_cursor OUT SYS_REFCURSOR) IS
v_count INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM module_list
WHERE module_id = p_module_Id
AND ROWNUM = 1;
IF v_count = 0 THEN
p_error_code := 0; -- Or -1403 for NO DATA FOUND if you like
p_error_msg := 'Does not Exists';
RETURN;
END IF;
OPEN p_Cursor FOR
SELECT module_id, module_nm, auth_status_id
FROM module_list
WHERE module_id = p_module_id;
EXCEPTION WHEN OTHERS THEN
p_error_code:= SQLCODE;
p_error_msg := SQLERRM;
END module_list_gk;
SQLCODE for the first query will be 0 by the way (SELECT COUNT(*) returns one row with the number of records found - no error hence). This is why you should decide to either return zero explicitly or some error code, such as -1403.
Here is the same with EXISTS:
BEGIN
SELECT CASE WHEN EXISTS
(
SELECT NULL
FROM module_list
WHERE module_id = p_module_Id
) THEN 1 ELSE 0 END
INTO v_count
FROM DUAL;
IF v_count = 0 THEN

What I am doing wrong in this procedure

I have created procedure to check and validate username and password, even when I provide correct password I will receive always exception error. I tried different thing inside the procedure but results would be the same.
create or replace
PROCEDURE member_ck_sp
(p_uname IN VARCHAR2,
p_pass IN VARCHAR2,
p_name OUT VARCHAR2,
p_cookie OUT VARCHAR2)
IS
CURSOR CUR_CHECK IS
SELECT USERNAME, PASSWORD,FIRSTNAME||''||LASTNAME, COOKIE
FROM bb_shopper;
lv_check_txt VARCHAR2(100);
BEGIN
FOR rec_check IN cur_check LOOP
IF p_uname = rec_check.username
AND p_pass = rec_check.PASSWORD THEN
lv_check_txt := 'Pass';
ELSE lv_check_txt := 'Fail';
END IF;
END LOOP;
IF lv_check_txt = 'Pass' THEN
SELECT FIRSTNAME||''||LASTNAME, COOKIE
INTO p_name, p_cookie
FROM bb_shopper
WHERE USERNAME = P_UNAME
AND password = p_pass;
dbms_output.put_line(p_name||' '|| p_cookie);
ELSE raise no_data_found;
END IF;
--dbms_output.put_line(p_name||' '|| p_cookie);
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('Please reneter credentials');
END;
And block to check code:
DECLARE
lv_username_txt bb_shopper.username%TYPE := 'rat55';
lv_password_txt bb_shopper.PASSWORD%TYPE := 'kile';
lv_name_txt VARCHAR2(200);
lv_cookie_txt bb_shopper.cookie%TYPE;
BEGIN
member_ck_sp(lv_username_txt,lv_password_txt,lv_name_txt,lv_cookie_txt);
--DBMS_OUTPUT.PUT_LINE('User name is '||lv_name_txt||' and
cookie '||lv_cookie_txt);
END;
Your problem is the opening LOOP reads all the records in bb_shopper. One of those records presumably matches the entered credentials. However, unless the last record read is the matching one, you will exit the loop with lv_check_txt = 'Fail'. And that's why you always fail the test in the subsequent IF and get no_data_found.
The solution seems quite simple: ditch the loop and just validate the passed parameters.
create or replace
PROCEDURE member_ck_sp
(p_uname IN VARCHAR2,
p_pass IN VARCHAR2,
p_name OUT VARCHAR2,
p_cookie OUT VARCHAR2)
IS
BEGIN
SELECT FIRSTNAME||''||LASTNAME, COOKIE
INTO p_name, p_cookie
FROM bb_shopper
WHERE USERNAME = P_UNAME
AND password = p_pass;
--dbms_output.put_line(p_name||' '|| p_cookie);
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20000, 'Please re-enter credentials');
END;
I haven't looked at PL\SQL in a long time. However, my first suggestion would be to look at your test data:
SELECT * FROM bb_shopper where username = 'rat55';
A few things to keep in mind:
The last line in the block to check code was probably meant to be commented out. It contains a quotation mark left open and a close bracket without an opening bracket. That can't help.
I'll take a different tack on this one. I see one potential error that overrides anything regarding the syntax and functionality, and that is:
I really really REALLY hope you are not planning on storing cleartext passwords in a database table.
Do not ever do this....ever. Please tell us that this routine already has the password salted/hashed before making it to this routine and table. Otherwise, this is the first thing you should looking at fixing before anything else.

How to make an Oracle procedure return a cursor with multiple rows in it

I have an Oracle procedure that returns a cursor with a result set in it but with the way I have it coded it only returns the results from the last iteration of the loop that I have.
Here is an example of the code that I have
create or replace PROCEDURE PROC_SB_OBTENER_STATS_TEST (FECHA_INICIO IN DATE, FECHA_FIN IN DATE, RESULTADOS OUT SYS_REFCURSOR) AS
FECHAS SYS_REFCURSOR;
FECHA_BUSQUEDA DATE;
BEGIN
FECHAS := FUN_SB_OBTENER_FECHAS(FECHA_INICIO, FECHA_FIN);
LOOP
FETCH FECHAS
INTO FECHA_BUSQUEDA;
EXIT WHEN FECHAS%notfound;
OPEN RESULTADOS FOR
--Here I got a select where the condition uses the variable FECHA_BUSQUEDA
END LOOP;
CLOSE FECHAS;
END PROC_SB_OBTENER_STATS_TEST;
The procedure compiles and it runs but it only returns me the cursor with the last select that it executes (if I put the dates between 10/10/12 and 12/10/12 it only returns me the result of 12/10/12).
How can I make the OPEN FOR add the result of a query to its current contents instead of overwriting it all and just showing me the last result?
CREATE OR REPLACE TYPE DATEARRAY IS TABLE OF DATE;
create or replace PROCEDURE PROC_SB_OBTENER_STATS_TEST (FECHA_INICIO IN DATE, FECHA_FIN IN DATE, RESULTADOS OUT SYS_REFCURSOR) AS
FECHAS SYS_REFCURSOR;
FECHA_BUSQUEDA DATEARRAY;
BEGIN
FECHAS := FUN_SB_OBTENER_FECHAS(FECHA_INICIO, FECHA_FIN);
FETCH FECHAS
BULK COLLECT INTO FECHA_BUSQUEDA;
OPEN RESULTADOS FOR
--Here I got a select:
SELECT * FROM YOUR_TABLE T WHERE T.DATE IN (SELECT v.COLUMN_VALUE FROM TABLE(FECHA_BUSQUEDA) v)
CLOSE FECHAS;
END PROC_SB_OBTENER_STATS_TEST;

Stored Procedure Related

I am creating a stored procedure in oracle that is selecting records from login table -
create or replace procedure login_info
(username IN varchar2, password IN varchar2, result OUT number)
as
begin
select * from login;
end;
Whenever I am going to compile this it shows an error:
PLS-00428: an INTO clause is expected in this SELECT statement
What does this mean? I do not understand this.
You have to store the result of your SELECT statement into a variable, you can use sys_refcursor to display the result.
create or replace procedure login_info
(username IN varchar2, password IN varchar2, result OUT number, result_out OUT SYS_REFCURSOR)
as
l_query varchar2(1000) := Null;
begin
l_query := 'select * from login';
open result_out
for l_query;
end;
above code will give you the output
PLS-00428: an INTO clause is expected in this SELECT statement
That means you need an INTO close when you issue bare SELECT from PL/SQL.
:D
More constructively: where do you think the result of your select would go in that code fragment?
create or replace procedure login_info
(username IN varchar2, password IN varchar2, result OUT number)
as
begin
select * from login;
end;
You have to retrieve it somehow in order to be processed by your PL/SQL code. Assuming you have several rows to collect, you should use BULK COLLECT INTO:
create or replace procedure login_info
(username IN varchar2, password IN varchar2, result OUT number)
as
type my_tbl_type IS TABLE OF login%ROWTYPE;
my_tbl my_tbl_type;
begin
select * BULK COLLECT INTO my_tbl from login;
-- do whatever you
-- need here
-- on `my_tbl`.
end;
As a final note, maybe are you looking for an explicit CURSOR instead? You should definitively take a look at PL/SQL 101: Working with Cursors. This is an interesting discussion both about SELECT ... INTO ... and CURSOR manipulation.

Resources