Converting Cursor from PLSQL into TSQL - sql-server

i m migrating my following plsql function from oracle into mssql 2008 but dont know how to convert cursor in a while loop.Could u help?
CREATE OR REPLACE function f_genel_iskonto (p_ID_MUSTERI_SIRKET in number, p_BILGI_TIP in NUMBER)
return number
is
v_iskonto number;
begin
v_iskonto:=null;
for c in (
SELECT an.mt_iskonto_oran, an.aktif, an.id_anlasma
FROM lu_anlasma an
WHERE an.id_musteri_sirket = p_ID_MUSTERI_SIRKET AND an.id_durum = 9
AND (TRUNC (SYSDATE) BETWEEN an.baslangic AND an.bitis)
ORDER BY an.baslangic DESC
) loop
if p_BILGI_TIP=1 then
v_iskonto:=c.mt_iskonto_oran;
end if;
if p_BILGI_TIP=2 then
v_iskonto:=c.aktif;
end if;
if p_BILGI_TIP=3 then
v_iskonto:=c.id_anlasma;
end if;
exit;
end loop;
return v_iskonto;
exception
when others then
return null;
end;

You don't have to convert a cursor/loop in this case as it only looks at the first record.
create function f_genel_iskonto (#p_ID_MUSTERI_SIRKET int, #p_BILGI_TIP int)
returns int
as
begin
declare #result int
SELECT top 1
#result = case #p_BILGI_TIP
when 1 then an.mt_iskonto_oran
when 2 then an.aktif
when 3 then an.id_anlasma
end
FROM lu_anlasma an
WHERE an.id_musteri_sirket = #p_ID_MUSTERI_SIRKET AND an.id_durum = 9
AND (current_timestamp BETWEEN an.baslangic AND an.bitis)
ORDER BY an.baslangic DESC
return #result
end
And you will probably need to tweak the date comparison to do what you want.

Related

select from table of varchar2

i have a type
create or replace TYPE "CUSTOM_DATA" IS TABLE OF VARCHAR2(4000) .
i can assign some values and print to output without problem
declare
cust_data CUSTOM_DATA;
begin
cust_data:=CUSTOM_DATA('A','B','C');
FOR i IN 1 .. cust_data.COUNT
LOOP
DBMS_OUTPUT.put_line (cust_data(i));
END LOOP;
end;
But how can i use cust_data in select clause , because i will assign them to a ref cursor , how can i do this? my code below is not working
SELECT COLUMN_VALUE as val FROM table(cust_data);
Your code works:
DECLARE
cust_data CUSTOM_DATA;
BEGIN
cust_data:=CUSTOM_DATA('A','B','C');
FOR i IN (SELECT COLUMN_VALUE as val FROM table(cust_data))
LOOP
DBMS_OUTPUT.put_line(i.val);
END LOOP;
END;
/
or:
DECLARE
cust_data CUSTOM_DATA;
cur SYS_REFCURSOR;
value VARCHAR2(4000);
BEGIN
cust_data:=CUSTOM_DATA('A','B','C');
OPEN cur FOR
SELECT COLUMN_VALUE as val FROM table(cust_data);
LOOP
FETCH cur INTO value;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.put_line(value);
END LOOP;
END;
/
Which both output:
A
B
C
db<>fiddle here

How to avoid no data exception?

I am working on a project and I have a database schema written in Oracle db for a library. I have some issues when trying to build a function which should provide some recommendations for the user.
The recommendations should be given based on the last books genres some user had borrowed in the past month and the most read books of all users. Every time I try to run the function I get a no data error even if I have values in the tables.
CREATE OR REPLACE FUNCTION getGenre(bookISBN IN VARCHAR2) RETURN VARCHAR2
AS
v_bookGenre VARCHAR2(100);
BEGIN
select genre into v_bookGenre from (select genre from books where bookISBN=isbn);
return v_bookGenre;
END getGenre;
/
CREATE OR REPLACE TYPE t IS TABLE OF varchar2(200);
/
create or replace type typeRecom is VARRAY(200) of VARCHAR2(200);
/
CREATE OR REPLACE FUNCTION topRecommandations(idUser IN VARCHAR2)
RETURN typeRecom
AS
lastGenres t :=t();
readedBooks t:=t();
topTen typeRecom;
v_i NUMBER := 1;
v_j NUMBER := 1;
verifDateBook NUMBER:=0;
verifDateLoan NUMBER:=0;
BEGIN
select count(*) into verifDateBook from books;
if(verifDateBook = 0) then
raise no_data_found;
end if;
select count(*) into verifDateLoan from loans;
if(verifDateLoan = 0) then
raise no_data_found;
end if;
lastGenres.extend();
for i in (select genre from books join loans on books.isbn=loans.bookId where loanDate>add_months(sysdate,-1) and idUser=loans.regNo order by genre desc) loop
DBMS_OUTPUT.PUT_LINE(i.genre);
lastGenres.extend();
lastGenres(lastGenres.count) := i.genre;
end loop;
/*select title into bookTitles from book join loan on book.isbn=loan.regNo;*/
readedBooks.extend();
for i in (select bookId from loans group by bookId order by count(bookId) desc) loop
readedBooks.extend();
readedBooks(readedBooks.count) := i.bookId;
end loop;
--select bookId into readedBooks from loan order by count(bookId) desc;
for v_i IN 1..50 LOOP
for v_j in 1..50 LOOP
DBMS_OUTPUT.PUT_LINE(lastGenres(v_j));
DBMS_OUTPUT.PUT_LINE(getGenre(readedBooks(v_i)));
if(lastGenres(v_j)=getGenre(readedBooks(v_i))) then
topTen(v_i) := getGenre(readedBooks(v_j));
end if;
EXIT WHEN topTen.count=10;
END LOOP;
EXIT WHEN topTen.count=10;
end LOOP;
for v_cont in 1..10 LOOP
--if(topTen.count <= 10) then
DBMS_OUTPUT.PUT_LINE(topTen(v_cont));
--end if;
end loop;
return topTen;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE('Nu exista valori.');
END topRecommandations;
Expected result: a list with 10 recommended books.
Actual result: no data found error.
Unless I'm wrong, the only place that might return NO_DATA_FOUND is the getGenre function, as it has a SELECT some value into a variable, and no exceptions are handled. Other SELECTs use aggregates (which will return 0 if there's nothing there).
Therefore, fix that function, e.g.
CREATE OR REPLACE FUNCTION getGenre(bookISBN IN VARCHAR2)
RETURN VARCHAR2
AS
v_bookGenre VARCHAR2(100);
BEGIN
select genre
into v_bookGenre
from books where bookISBN = isbn;
return v_bookGenre;
EXCEPTION
when no_data_found then
return null;
END getGenre;
By the way, why did you use an inline view in that function? Why didn't you simply return genre from the table itself (like I did)?

Oracle PL/SQL Assign each value from a cursor(from function) to another cursor one by one

I have a function called GET_CLIENT_IN_SED(return sys_refcursor), it gives me a list of id numbers(single column). Now, in a procedure, I am trying to loop through each (one by one) of that values and use it for calling a second procedure (it needs a client id parameter).
PROCEDURE GET_ORDINARY_CLIENT;
PROCEDURE GET_ORDINARY_CLIENT_BY_SED
( sed_in IN varchar2, client_sed OUT SYS_REFCURSOR )
IS
ordinary_clients sys_refcursor;
BEGIN
ordinary_clients := GET_CLIENT_IN_SED(sed_in);
for item in ordinary_clients loop
client_sed := client_sed + ordinary_clients(i);
end loop;
END;
As far i could understand you need to do something like :
Function:
This function would take input as number and return a refcursor. Similar to your requirement.
CREATE OR REPLACE FUNCTION get_num_sysrefcur (num IN NUMBER)
RETURN SYS_REFCURSOR
AS
my_cursor SYS_REFCURSOR;
BEGIN
--
OPEN my_cursor FOR
WITH ntable
AS (SELECT 1 ID, 111 AGT, 'ABC' DESCRIP FROM DUAL
UNION ALL
SELECT 2 ID, 222 AGT, 'ABC' DESCRIP FROM DUAL
UNION ALL
SELECT 1 ID, 333 AGT, 'ABC' DESCRIP FROM DUAL)
SELECT AGT FROM ntable WHERE ID = num;
RETURN my_cursor;
END;
/
Block ( In your case Procedure )
-- This anonymous block will loop through the records return from the sys_refcursor. Similiar to you need where you want the second procedure to use the value of sys_refcursor and loop it(You can create procedure in place of this anonymous block).
DECLARE
a NUMBER := 1;
TYPE ta IS TABLE OF NUMBER
INDEX BY PLS_INTEGER;
b ta;
x SYS_REFCURSOR;
BEGIN
x := get_num_sysrefcur (a);
fetch x bulk collect into b;
for i in 1..b.count
loop
-- Displaying the result of the ref_cursor.
DBMS_OUTPUT.put_line (b(i));
end loop;
END;
To loop through a ref cursor is not like looping through an array or table which explains why your FOR...LOOP is not working.
In short, instead of a collection, the ref_cursor is more of a "pointer" or an "iterator" over a collection. In this other question you will find a quite clear example of iterating through a ref_cursor using FETCH.
How to use record to loop a ref cursor?
An example with your data would look like this :
PROCEDURE GET_ORDINARY_CLIENT_BY_SED(sed_in IN VARCHAR2,
client_sed OUT SYS_REFCURSOR) IS
ordinary_clients SYS_REFCURSOR;
clt NUMBER; -- assuming your cursor contains strictly numbers
BEGIN
ordinary_clients := GET_CLIENT_IN_SED(sed_in);
LOOP
FETCH ordinary_clients
INTO clt;
EXIT WHEN ordinary_clients%NOTFOUND;
dbms_output.put_line(clt);
-- do some other things here with your number
END LOOP;
END;

Stored procedure in T-SQL transform to PostgreSQL

I have a stored procedure writen in T-SQL and I want to make it for PostgreSQL but I'm not so familiar with PostgreSQL.
My stored procedure look like this:
CREATE PROCEDURE usp_insert_allocated_time
#fld_project_id INT,
#fld_allocated_time INT
AS
DECLARE #project int;
SET #project = #fld_project_id;
DECLARE #allocated int;
DECLARE #time int;
BEGIN
SET #time = (SELECT SUM(fld_allocated_time)
FROM dbo.tbl_project_timesheet
WHERE fld_project_id =#project)
SET #allocated = (SELECT fld_allocated_days FROM dbo.tbl_project where fld_id = #project);
IF #allocated > #time
BEGIN
INSERT into dbo.tbl_project_timesheet(fld_project_id,fld_allocated_time)
VALUES(#fld_project_id,#fld_allocated_time);
END
ELSE
PRINT 'Not OK';
END
And I have to do something like this, but on line 10 I get this error:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function
"SA_PRJ".usp_add_timesheet_record_new(integer,integer,numeric,numeric,character varying,character varying) line 10 at assignment
CREATE OR REPLACE FUNCTION "SA_PRJ".usp_add_timesheet_record_new(p_uid integer, p_project_id integer, p_allocated_time numeric, p_achieved_time numeric, p_task_desc character varying, p_obs character varying)
RETURNS void AS
$BODY$
declare alloc_id integer;
declare project integer;
declare allocated integer;
declare allocated_time integer;
BEGIN
project := p_project_id;
allocated_time := (SELECT SUM(fld_allocated_time)
FROM "SD_PRJ".tbl_project_timesheet
WHERE fld_project_id = project);
allocated := (SELECT fld_allocated_days FROM "SD_PRJ".tbl_project where fld_id = project);
if not "SA_ADM".usp_check_permission(p_uid, 'SA_PRJ', 'usp_add_timesheet_record') then
raise exception 'User ID % no have the permission!', p_uid;
end if;
select fld_id into alloc_id from "SD_PRJ".tbl_project_allocation where fld_emp_id = p_uid and fld_project_id = p_project_id;
BEGIN
IF (allocated > allocated_time) THEN
INSERT INTO "SD_PRJ".tbl_project_timesheet(fld_emp_id, fld_project_id, fld_is_allocated,fld_allocated_time, fld_achieved_time, fld_task_desc, fld_obs)
VALUES (p_uid,p_project_id,coalesce(alloc_id,0), p_allocated_time, p_achieved_time,p_task_desc, p_obs);
ELSE
RAISE NOTICE 'Not OK!!';
END IF;
END;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
It's more complex version in PostgreSQL for what I want.
You don't really give enough information to try and fix your problem, but the error message is pretty descriptive. You are trying to put 293.00 into an integer. Here I can reproduce:
DO
$$
DECLARE
i INT;
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This will raise:
ERROR: invalid input syntax for integer: "293.00"
SQL state: 22P02
Context: PL/pgSQL function inline_code_block line 6 at assignment
You need to change your variable to the same datatype as the data you are trying to assign to it. For example:
DO
$$
DECLARE
i NUMERIC(5, 2);
BEGIN
i := 293.00;
RAISE NOTICE 'i=%', i;
END
$$;
This works and outputs:
NOTICE: i=293.00
Query returned successfully with no result in 14 ms.

Declare local variable: how convert this mssql script to oracle?

declare #amount float
declare #result char (20)
select #amount = cost from PurchaseDoc where id = 1
if #amount > 0 set #result = 'ok'
else set #result = 'empty'
print #result
Here is one representation of your script which can be executed against an Oracle database:
DECLARE
amount NUMBER;
result varchar2(20);
BEGIN
SELECT SUM(cost) INTO amount
from PurchaseDoc
WHERE id = 1;
if (amount > 0)
then
result := 'ok';
else
result := 'empty';
end if;
dbms_output.put_line(result);
END;
/
See this link for some useful information about dbms_output.
I recommend taking a look at some PL/SQL tutorials, such as the ones located at www.plsql-tutorial.com.
EDIT
Updated select statement based on suggestion by Cade Roux
There is really no need for PL/SQL here, a normal select statement with 'decode' function can do the trick.
SELECT DECODE(SUM(COST),0,'empty','ok') FROM PurchaseDoc where id = 1;

Resources