help, stored procedures and cursors - sql-server

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

Related

How can I add defined parameter to (NEXT VALUE FOR) command?

CREATE OR ALTER FUNCTION sso.FINDSEQVALUE
(#sequence_text text)
RETURNS int
AS
BEGIN
DECLARE #value int;
DECLARE #sequence_value nvarchar(150);
SELECT #sequence_value = CAST(#sequence_text AS nvarchar(150));
SELECT #value = NEXT VALUE FOR #sequence_value;
RETURN #value;
END;
I have a problem. I have created a function on SQL Server and I defined the parameter as you can see. But I cannot add the this command #sequence_value after NEXT VALUE FOR command and I am getting an error.
Incorrect syntax near '#sequence_value'
Somebody can say that "You can use (SELECT NEXT VALUE FOR [SEQUENCE])". But I need this function because of there are two different database on my project. I need same function for databases. In addition function parameter need to be text.
What should I do?

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.

Cursor with a variable as a parameter Oracle

I have a function with a cursor. Within this cursor I want to get another cursor which I pass a parameter . This parameter is a value of the primary cursor. The logic that I have is like this:
CURSOR cursor1 IS
SELECT * FROM SCHEMAP.TABLA1 ;
registro cursor1%ROWTYPE;
CURSOR cursor2 (parametro IN NUMBER) IS
SELECT * FROM SCHEMAP.TABLA2 WHERE CAMPO_1 = parametro;
registroVac cursor2%ROWTYPE;
..........
BEGIN
.......
OPEN cursor1;
FETCH cursor1 INTO registro;
WHILE cursor1%found
LOOP
dbms_output.put_line('VARIABLE1:' + registro.VARIABLE1 );
OPEN cursor2(registro.VARIABLE1);
FETCH cursor2 INTO registroVac;
WHILE cursor2%found
LOOP
SELECT HC3PKDMUTILITIES.GET_DIAGNOSTIC_CODE_VAC(registro.VARIABLE1,registroVac.VAC_DOS,registroVac.VAC_CVH)
into v_diagnostic_code
from DUAL;
dbms_output.put_line('v_diagnostic_code -->' || v_diagnostic_code);
FETCH cursor2 INTO registroVac;
END LOOP;
CLOSE cursor2;
FETCH cursor1 INTO registro;
END LOOP;
CLOSE cursor1;
When I run the process I have an error in cursor2 like this:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error CAMPO_1:102435313
CAMPO_1 is proven to be a numerical Database and registro.VARIABLE1 too. How to solve this problem?. Thanks.
Is this only a code-snipped and do you have more code in your application?
If not, then you can do it all in one which should be much faster and shorter:
CURSOR All_in_one IS
SELECT VARIABLE1,
HC3PKDMUTILITIES.GET_DIAGNOSTIC_CODE_VAC(registro.VARIABLE1,registroVac.VAC_DOS,registroVac.VAC_CVH) AS v_diagnostic_code
FROM SCHEMAP.TABLA2 t registroVac
RIGHT OUTER JOIN SCHEMAP.TABLA1 registro ON CAMPO_1 = VARIABLE1;
There are loads of things wrong with your code.
The thing that strikes me first is that you're opening cursor1, opening, fetching and closing cursor2, and then fetching from cursor1. That seems incorrect!
Secondly, why bother having two cursors and reinventing nested loop joins yourself? SQL is perfectly capable of handling joins, and the optimizer is pretty good at deciding which join to use (hash join, nested loops, etc).
Thirdly, if HC3PKDMUTILITIES.GET_DIAGNOSTIC_CODE_VAC is a function, why are you selecting it from dual (and therefore introducting context switching between the PL/SQL and SQL engines), rather than simply assigning the variable with the return value of the function?
I think you could rewrite the above code as simply:
declare
v_diagnostic_code varchar2(100); -- wasn't sure of the correct datatype; this is just a guess
begin
for rec in (select t1.variable1,
t2.vac_dos,
t2.vac_cvh
from schemap.tabla1 t1
inner join schemap.tabla2 t2 on (t2.campo_1 = t1.variable1))
loop
v_diagnostic_code := hc3pkdmutilities.get_diagnostic_code_vac(rec.variable1,rec.vac_dos,rec.vac_cvh);
dbms_output.put_line('variable1 --> '||rec.variable1||', v_diagnostic_code -->' || v_diagnostic_code);
end loop;
end;
/

SQL Server Function/Proc evaluate an expression(s) return an error if not valid

I would like to take the code below and create a common function to pass in one or more expressions and to return back an error of my choosing.
Example Code:
IF #Variable1 IS NULL AND #Variable IS NULL or #Variable3 is not null
BEGIN
-- EITHER DATASET NAME OR ID MUST BE SUPPLIED.
SET #_msg = 'There was an error'
SET #_returnValue = -1
GOTO ERROR_HANDLER
END
ERROR_HANDLER:
-- CREATE THE CLOSING MESSAGE.
IF #_returnValue <> 0
RAISERROR(#_msg, 18, 2) WITH SETERROR
RETURN #_returnValue
From the above, it would be nice to say something like this below where I could reuse the proc/function and make the code less clutered.
exec ValidateMultipleConditions #Variable1 + 'IS NULL AND ' + #Variable + 'IS NULL or ' + #Variable3 + ' is not null'
Anyway, I think with dynamic SQL being passed in this way I could do something where an complete expression could be sent evaluated, validated and then the code continues or stops with an error.
I wanted to see if the community had better ways of doing this or if I'm on the right path.
Thanks.
I'm not quite sure if I get your question right. But you can easily create an procedure (if it's needed).
CREATE PROCEDURE dbo.errorout #message nvarchar(100), #sev int, #state int
AS
BEGIN
RAISERROR(#message,#sev,#state) WITH NOWAIT
END
But I won't use this at all. I would call RAISERROR() in the place where it occurs, as it will give you more accurate line numbers and procedures in the errorlog.

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

Resources