Exit from a stored procedure - sql-server

I have some loop and a condition. If codition is matched then I want to stop or exit from the stored procedure. How to do that?
while ##fetch_status=0
begin
if x=0
'exit stored procedure
end

if you are using Microsoft Sql Server than you can use Return Statement
while ##fetch_status=0 begin if x=0 return; end

By ##fetch_status it looks like your inside a cursor loop so I would not return at that point as you will skip tidying up after yourself.
...
if x=0
GOTO DONE
...
/* at the end of the sp */
DONE:
CLOSE #your_cur
DEALLOCATE #your_cur

try use return
while ##fetch_status=0
begin
if x=0
return
end

Related

Cursor is returning more data that is available in table

I have query
select * from ot.city_vw;
The data coming is:
But when I write a pl/sql block to get the data then
declare
v_data OT.city%rowtype;
CURSOR cur1 is
select * from ot.city_vw;
begin
open cur1;
loop
fetch cur1 into v_data;
dbms_output.put_line(v_data.city_id);
dbms_output.put_line(v_data.city_name);
EXIT WHEN cur1%NOTFOUND;
END LOOP;
CLOSE cur1;
end;
/
the last table data is coming twice as below in picture:
what is the problem with my cursor? why is the last data coming twice?
Move EXIT up.
open cur1;
loop
fetch cur1 into v_data;
EXIT WHEN cur1%NOTFOUND; --> here
dbms_output.put_line(v_data.city_id);
dbms_output.put_line(v_data.city_name);
END LOOP;
CLOSE cur1;
Or, even better, user cursor FOR loop:
begin
for v_data in (select * from ot.city_vw)
loop
dbms_output.put_line(v_data.city_id);
dbms_output.put_line(v_data.city_name);
end loop;
end;
Doesn't it look simpler? Oracle does all the dirty job for you (declaring a cursor variable, opening the cursor, exiting the loop, closing the cursor). I suggest you use it whenever possible.

PLSQL: IF EXISTS in stored procedure while using loop

I am new to PLSQL. I am trying to create a procedure which iterates through an array.
My requirement is if one of the value is not found in table, it should add into FAILARRAY, otherwise it should add into PASSARRAY.
I was getting no data found exception even if it is handled, it goes out of the loop and next value in the loop is not getting iterated again.
Is there any way we can use if exists command here. Please help.
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE
(
FILEARRAY IN STRARRAY,
PASSARRAY OUT STRARRAY,
FAILARRAY OUT STRARRAY,
)
IS
--DECLARE
fileName VARCHAR2 (50);
fileId NUMBER;
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY (i));
SELECT FILEID into fileId FROM TABLE_NAME WHERE FILENAME=fileName;
end loop
END;
I suspect you haven't realised that you can have a PL/SQL BEGIN ... END block, including an exception handler, within a loop. In fact, anywhere you can have PL/SQL statements you can have a block.
You mention an exception handler, although your code doesn't contain one. As you say your code goes 'out of the loop', I can only assume it's, well, outside of the for loop. But you can easily add a block, with an exception handler, inside the for loop, for example:
BEGIN
for i in 1 .. FILEARRAY.count
loop
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY (i));
-- Inner block starts at the line below:
BEGIN
SELECT FILEID into fileId FROM TABLE_NAME WHERE FILENAME=fileName;
-- TODO add to PASSARRAY
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- TODO add to FAILARRAY
END;
end loop
END;
This way, if there are 8 values in FILEARRAY and no data is found in the table for the third value, the NO_DATA_FOUND exception gets caught without exiting the loop and the loop then progresses to the fourth value in FILEARRAY.
You are handling the exception but you need to avoid the exception. Try:
SELECT NVL(FILEID, "<Put Something here or leave it empty") FROM TABLE_NAME WHERE FILENAME=fileName;
That way if it finds a null value in the select it will just pull "" instead. Then you can check to see if your SELECT returns "" and if so populate your FAILARRAY, otherwise populate PASSARRAY.
CREATE OR REPLACE PROCEDURE SCHEMA.PR_VALIDATE(
FILEARRAY IN STRARRAY,
PASSARRAY OUT STRARRAY,
FAILARRAY OUT STRARRAY )
IS
fileName VARCHAR2 (50);
l_n_count NUMBER;
l_n_file_id NUMBER;
BEGIN
FOR i IN 1 .. FILEARRAY.count
LOOP
fileName := FILEARRAY(i);
DBMS_OUTPUT.put_line (FILEARRAY(i));
SELECT COUNT(FILEID) INTO l_n_count FROM TABLE_NAME WHERE FILENAME=fileName;
IF l_n_count =0 THEN
failarray(i):='No Value Found';
elsif l_n_count=1 THEN
SELECT FILEID INTO l_n_file_id FROM TABLE_NAME WHERE FILENAME=fileName;
Passarray(i):=l_n_file_id;
END IF;
END LOOP;
END;
/

ABout "not in" in SQL Server 2008

I have a problem with SQL query.
This is my code:
set #TimeComing=(Select TimeComing from Bill)
set #TimeCome= DATEPART(hour,#TimeComing)
if(#TimeCome>=22 and #TimeCome<=23)
begin set #Bill=#ByDay set #Day=1 set #Hour=0 end
if(#TimeCome>=21 and #TimeCome<22)
begin set #Bill=#ByHour set #Day=0 set #Hour=1 end
if(#TimeCome>=21 and #TimeCome<=23 and #TimeCome not in (#TimeCome>=21 and #TimeCome<22))
begin set #Bill=#ByDay set #Day=1 set #Hour=0 end
As you see, i want to the 3rd condition - TimeCome not in 21:00 and 22:00 to set Bill=ByDay and Day=1. Because of 2nd Condition is used this Parameter. But i have an error
"Incorrect syntax near '>'."
Please help me this problem, thank you !
Update
This is my idea:
If customer come in 21:00 and go in 22:00 (21:00 to 22:00) => #Bill=#ByHour.
If customer come in 22:00 and go in 23:00 (22:00 to 23:00) => #Bill=#ByDay.
If customer come in 21:00 and go in 23:00 (21:00 to 23:00) => #Bill=#ByDay.
This should be:
if(#TimeCome>=21 and #TimeCome<=23 and not (#TimeCome >= 21 and #TimeCome<22))
But it can be simplified:
if(#TimeCome>=22 and #TimeCome<=23)
But you have already have this as first if statement...
Try this:
set #Bill=#ByDay
if(#TimeCome>=22 and #TimeCome<=23)
begin
select #Day=1, #Hour=0
end
if(#TimeCome>=21 and #TimeCome<22)
begin
select #Day=0, #Hour=1
end
Explanation:
I've moved the set #Bill=#ByDay part outside of the condition since it's the same on every if.
I've removed your third condition entirely, since the first condition is actually the same as what you said you want for the third one.
Now, about the IN operator: it expects a comma delimited list of values, not to be confused with a single variable that contains a comma delimited string!,
so a correct use of it would be and #TimeCome not in (21,22).
Note that something like and #TimeCome not in ('21,22') would not return the results you want.
However, you don't seem to need it at all, as I've already written.
Try this ie, your 3rd condition can be reduced down to this:
if(#TimeCome>=22 and #TimeCome<=23)
EDIT:
I think you dont need the 3rd condition at all. As 1st conditon is equivalent to the 3rd condition.
EDIT:
Try this:
declare #flag = false
if(#TimeCome>=22 and #TimeCome<=23)
begin set #Bill=#ByDay set #Day=1 set #Hour=0 end
if(#TimeCome>=21 and #TimeCome<22)
begin set #Bill=#ByHour set #Day=0 set #Hour=1 set #flag = true end
if(#TimeCome>=21 and #TimeCome<=23 and (#flag = false))
begin set #Bill=#ByDay set #Day=1 set #Hour=0 end
First we cannot use directly where condition as you have done above in the 3rd statement, it will give error for incorrect syntax
You can use not in condition by two ways
1) select sub-query in not in condition, here by updating third statement to
if(#TimeCome>=21 and #TimeCome<=23 and #TimeCome not in
(select #TimeCome where #TimeCome >= 22)) begin set #Bill=#ByDay set #Day=1 set #Hour=0
end
2) by setting values explicitly, here as below
if(#TimeCome>=21 and #TimeCome<=23 and #TimeCome not in (#TimeCome1, #TimeCom2)

If I open a Cursor in side a TRY then where should I close it?

If I have this:
BEGIN TRY
OPEN CUR1
FETCH NEXT FROM CUR INTO ...
END TRY
BEGIN CATCH
SET #RC = 1
SET #ErrorMessage = ERROR_MESSAGE()
RETURN
END CATCH
SET #RC = 0
RETURN
Should I close the Cursor inside the try or does it not matter. What I am unsure of is if my code in the try goes into the CATCH then the cursor may not get closed.
How is this normally dealt with? Also is they way I have my RETURN after the CATCH the normal way to code a return
Thanks
You should define your cursors as LOCAL if there's no need for having them as global.
Meaning of LOCAL from BOL:
Specifies that the scope of the cursor is local to the batch, stored
procedure, or trigger in which the cursor was created. The cursor name
is only valid within this scope. The cursor can be referenced by local
cursor variables in the batch, stored procedure, or trigger, or a
stored procedure OUTPUT parameter. An OUTPUT parameter is used to pass
the local cursor back to the calling batch, stored procedure, or
trigger, which can assign the parameter to a cursor variable to
reference the cursor after the stored procedure terminates. The cursor
is implicitly deallocated when the batch, stored procedure, or trigger
terminates, unless the cursor was passed back in an OUTPUT parameter.
If it is passed back in an OUTPUT parameter, the cursor is deallocated
when the last variable referencing it is deallocated or goes out of
scope.
https://msdn.microsoft.com/en-us/library/ms180169.aspx
I would close it before then END TRY. Additionally, I would add a check if the cursor is still open and close it if it is. See here for more info: http://www.sqlservercentral.com/Forums/Topic767778-338-1.aspx
BEGIN TRY
OPEN CUR1
FETCH NEXT FROM CUR INTO ...
CLOSE CUR1
DEALLOCATE test_cursor
END TRY
BEGIN CATCH
SET #curStatus = Cursor_Status('local', 'CUR1'); --set it to LOCAL above, if using global above change here too
IF #curStatus >= 0
BEGIN
CLOSE objectsCur;
DEALLOCATE objectsCur;
END
ELSE IF #curStatus = -1 --may have been closed already so just deallocate
BEGIN
DEALLOCATE objectsCur;
END
END CATCH
You will need to close the cursor inside the block that it has been declared, that is the TRY but it should also be deallocated in the error handler, that is the catch part;
BEGIN TRY
OPEN CUR1
FETCH NEXT FROM CUR INTO ...
CLOSE CUR1;
DEALLOCATE CUR1;
END TRY
BEGIN CATCH
SET #RC = 1
SET #ErrorMessage = ERROR_MESSAGE()
CLOSE CUR1;
DEALLOCATE CUR1;
RETURN
END CATCH
SET #RC = 0
RETURN

help, stored procedures and cursors

i have to write a stored procedure, where you give the month, and a credit card number, and it calulates 1% for each transaction made in the first 10 days of the month, 2% for transactions between 10 and 20, and 3% for transactions above 20.
And i must use cursors.
i wrote this code, but i get some errors when i try to run the precedure
create procedure cardP
/* month ex 1,3,5 etc*/
#minas int,
#cardNo bigint
as
/* creating the cursor*/
DECLARE sinallages CURSOR
FOR SELECT cc_number,day([DateTime]),charged_amount FROM transactions
where cc_number=#cardNo and month([DateTime])=#minas
/* declaring local variables*/
declare #poso int,#karta bigint,#mera int,#pos float,#pos2 float,#pos3 float,
#count int,#counter int
open sinallages
set #count=(select count(cc_number) from transactions where cc_number=#cardNo and month([DateTime])=#minas )
/* get 1st row*/
fetch sinallages into #karta,#mera,#poso
while (/*##sqlstatus != 2*/#counter<#count)
begin
if day(#mera)<=10
set #pos =#poso+ #poso * 0.01
else
if day(#mera)>10 and day(#mera)<=20
set #pos2 =#poso+ #poso * 0.02
else
if day(#mera) > 20
set #pos3 =#poso+ #poso * 0.03
fetch sinallages into #karta,#mera,#poso
set #counter=#counter+1
end
close sinallages
return
when i call the procedure i get
EXEC cardP #minas = 5, #cardNo =4929569752542450
Msg 16915, Level 16, State 1, Procedure cardP, Line 20
A cursor with the name 'sinallages' already exists.
Msg 16922, Level 16, State 1, Procedure cardP, Line 31
Cursor Fetch: Implicit conversion from data type datetime to int is not allowed.
thank you :) i now deallocate the cursor at the end of the stored procedure and removed the day(). Now i want to print the pos+pos2+pos3. I use print pos+pos2+pos3 but it doesnt print anything. why is that ??
................
set #counter=#counter+1
end
print #pos+#pos2+#pos3
close sinallages
return
DEALLOCATE sinallages;
it seems like hte variables po,pos2,pos3 are left null??
Others have suggested using DEALLOCATE. The problem with that is that, in some error situations, it won't be called. If you then attempt to use the same connection to call this stored proc, the cursor will still be allocated.
I'd prefer to instead declare the cursor as LOCAL, which means it's automatically deallocated when the stored proc is exited (whether normally or not).
Yes, you need to deallocate the cursor after closing it. Besides, if your query had an error before closing the cursor, it may have stayed open, so i recommend you execute the CLOSE and DEALLOCATE before executing your procedure again. For your second error, you are using the function DAY() over a variable of type INT, change if day(#mera)<=10 with if #mera<=10.
Update: Now that you fixed the problems you had, when you add each #pos variable, following your logic, one of them is always null, so you should add them like this:
Print isnull(#pos1,0)+isnull(#pos2,0)+isnull(#pos3,0)
After
close sinallages
You need to call
deallocate sinallages
Have a look at DEALLOCATE (Transact-SQL)
you have to dealocate the cursor after closing it:
DEALLOCATE sinallages
http://msdn.microsoft.com/en-us/library/ms188782.aspx

Resources