Oracle select inside an IF statement in a stored procedure - oracle

Coming from a sql server background, I am trying to get the grasp of oracle syntax. I am trying to return some records back from a stored procedure but getting an error:
CREATE OR REPLACE PROCEDURE SP_GetCustomers(username IN VARCHAR2)
AS
BEGIN
if username = 'all' then
select *
from customers c;
else
select *
from customers c
where c.created_by = username;
end if;
END;
What am I missing?

you are missing an INTO clause, but at this stage its better to use a FUNCTION to return values and a PROCEDURE to perform action.

ORACLE doesn't create implicit cursors as a return parameter, you will have to explicitly declare a ref cursor and open it to return a pointer to a cursor, something like:
CREATE OR REPLACE Function SP_GetCustomers(username IN VARCHAR2) return sys_refcursor
AS
ret_cursor sys_refcursor;
BEGIN
if username = 'all' then
open ret_cursor for
'select *
from customers c';
else
open ret_cursor for
'select *
from customers c
where c.created_by = :username' using username ;
end if;
return ret_cursor;
END;

Related

PL/SQL Function - Bulk Collect into and Pipe Row

I am new to PL/SQL have issue with output value of a function.
I want to execute a SQL statement in a function and return the results. The function will be executed with following command: select * from table(mypkg.execute_query('1'));
I was using following article as refence "Bulk Collect Into" and "Execute Immediate" in Oracle, but without success.
It seems that I am using wrong data type. System returns issue on following line: PIPE Row(results)
create or replace package mypkg
as
type node is table of edges%ROWTYPE;
function execute_query (startNode in varchar2) RETURN node PIPELINED;
end;
create or replace package body mypkg
as
function execute_query(startNode in varchar2) RETURN node PIPELINED
AS
results node;
my_query VARCHAR2(100);
output VARCHAR2(1000);
c sys_refcursor;
BEGIN
my_query := 'SELECT DISTINCT * FROM EDGES WHERE src='|| startNode;
open c for my_query;
loop
fetch c bulk collect into results limit 100;
exit when c%notfound;
PIPE Row(results);
end loop;
close c;
END;
end;
I tried several options with cursor but wasn't able to return the value. If you have idea how to return the data by using something else than PIPELINED, please let me know.
Thanks for your support!
Fixed body:
create or replace package body mypkg
as
function execute_query(startNode in varchar2) RETURN node PIPELINED
AS
results node;
my_query VARCHAR2(100);
output VARCHAR2(1000);
c sys_refcursor;
BEGIN -- don't use concatenation, it leads to sql injections:
my_query := 'SELECT DISTINCT * FROM EDGES WHERE src=:startNode';
-- use bind variables and bind them using cluase "using":
open c for my_query using startNode;
loop
fetch c bulk collect into results limit 100;
-- "results" is a collection, so you need to iterate it to pipe rows:
for i in 1..results.count loop
PIPE Row(results(i));
end loop;
exit when c%notfound;
end loop;
close c;
END;
end;
/

Error(4,5): PLS-00428: an INTO clause is expected in this SELECT statement

create or replace PROCEDURE find_Doctor (p_SSN in number) AS
BEGIN
select drName, drPhone
from clients
where SSN = p_SSN;
END find_Doctor;
I've got this stored procedure and I just want to output the resulting table from that call. Is there an easy way to do this without declaring a temporary table? I can't just make is a normal SQL query because I have to call it from a java program.
In a Procedure you would need variable to hold the result output of the SQL query. You can then use the variable. Use this:
CREATE OR REPLACE PROCEDURE find_Doctor (p_SSN IN NUMBER)
AS
var_nm VARCHAR2 (100);
var_ph NUMBER;
BEGIN
SELECT drName, drPhone
INTO var_nm, var_ph
FROM clients
WHERE SSN = p_SSN;
DBMS_OUTPUT.put_line ('Doc Name - ' || var_nm || 'Doc Ph. No-' || var_ph);
END find_Doctor;
Edit:
I can't just make is a normal SQL query because I have to call it from
a java program.
You can then use SYS_REFCUSOR to return results, which can be mapped to a JDBC ResultSet.
CREATE OR REPLACE PROCEDURE find_Doctor (p_SSN IN NUMBER,
VAR OUT SYS_REFCURSOR)
AS
BEGIN
OPEN VAR FOR
SELECT drName, drPhone
FROM clients
WHERE SSN = p_SSN;
END find_Doctor;
You should define out parameters drName and drPhone:
create or replace PROCEDURE find_Doctor (p_SSN in number, p_drName OUT
VARCHAR2, p_drPhone OUT VARCHAR2) AS
BEGIN
select drName, drPhone
into p_drName, p_drPhone
from clients
where SSN = p_SSN;
END find_Doctor;
Easy way is to learn tool you're used and best practices for it.
For me, best way is remove senseless procedure and just selects data you need. But if you adherent of 'SP only' approach you can use ref cursor to retrive required data:
create or replace function find_Doctor (p_SSN in number)
return sys_refcursor as
v_result sys_refcursor;
BEGIN
open v_result for
select drName, drPhone
from clients
where SSN = p_SSN;
return v_result;
END find_Doctor;

Oracle - open a sys_refcursor for an explicit cursor

I have a function that returns a sys_refcursor so that a Java application can process the results:
function fn_stuff (in_array in t_id_array) return sys_refcursor is
rc sys_refcursor;
begin
open rc for
--query has been significantly shortened since most details are not
--relevant for this question.
select * from some_table where id in table(in_array);
return rc;
end;
I'd also like to be able to use this function with PL/SQL. It would be very convenient to be able to reference the rowtype of this result like this:
procedure sp_f is
rc sys_refcursor;
arr t_id_array ;
r c_stuff%rowtype;
begin
arr := t_id_array('11','22','33','44');
rc := pkg_stuff.fn_stuff(arr);
loop
fetch rc into r
exit when rc%notfound;
dbms_output.put_line(r.col1||' '||r.col2);
end loop;
end;
To do this, I'd need to declare the SELECT statement in fn_stuff as a proper cursor:
cursor c_stuff (in_array in t_id_array) is
select * from some_table where id in table(in_array);
So then I thought to update the function to simply reference the new cursor I declared:
function fn_stuff (in_array in t_id_array) return sys_refcursor is
rc sys_refcursor;
begin
open rc for
select * from c_stuff(in_array);
return rc;
end;
Which yields:
Error(52,37): PL/SQL: ORA-00904: : invalid identifier
I could keep the SELECT statement as it was inside fn_stuff but it's still in development so it would be easier to just have the query in a single place.
Is there any way to open and return a sys_refcursor on a declared cursor?
I guess another option is that I could declare a record type which has all of the columns that are selected by the query, but since the query is still under development and columns may be added ore removed from the select, the record type would have to be kept in sync. But since the sp_f procedure only is interested in a handful of columns that probably won't change, I thought the %rowtype would be better.

How to describe a reference cursor associated with dynamic SQL in Oracle?

This is my first (edited) stackoverflow question, so please bear with me.
In Oracle 11g, I have a need to describe/interrogate the underlying columns of a reference cursor returned from a procedure call on another database over a dblink, in which the actual SQL is not always "explicit", but sometimes dynamically generated.
For example:
declare
v_ref_cur sys_refcursor;
v_cur_num number;
v_col_count number;
v_col_table dbms_sql.desc_tab3;
begin
myProc#myDblink(v_ref_cur, 'myvalue');
v_cur_num := dbms_sql.to_cursor_number(v_ref_cur);
dbms_sql.describe_columns3(v_cur_num, v_col_count, v_col_table);
...
end
If myProc() on the other database has an "explicit" SQL statement like:
open cursor for select * from foo where bar = myParam;
The cursor conversion and description (still) work just fine - I can determine the column names, types, lengths, etc returned by the procedure.
BUT, if myProc() on the other database involves dynamic SQL, like:
v_sql := 'select * from foo where bar = ''' || myParam || '''';
open cursor for v_sql;
I get an ORA-01001 invalid cursor error when attempting to call dbms_sql.to_cursor_number().
Is there a way to convert/describe a reference cursor derived from dynamic SQL as called from a remote procedure? If so, how? If not, why not?
Thanks for any/all assistance!
Using DBMS_SQL across a database link raises many different errors, at least some of which are Oracle bugs. Those problems can be avoided by putting all of the logic in a function compiled on the remote server. Then call that function remotely.
--Create and test a database link
create database link myself connect to <schema> identified by "<password>"
using '<connect string or service name>';
select * from dual#myself;
--myProc
create procedure myProc(p_cursor in out sys_refcursor, p_value varchar2) is
begin
--open p_cursor for select * from dual where dummy = p_value;
open p_cursor for 'select * from dual where dummy = '''||p_value||'''';
end;
/
--Simple function that counts and displays the columns. Expected value is 1.
create or replace function count_columns return number is
v_ref_cur sys_refcursor;
v_cur_num number;
v_col_count number;
v_col_table dbms_sql.desc_tab3;
begin
--ORA-01001: invalid cursor
--myProc#myself(v_ref_cur, 'myvalue');
myProc(v_ref_cur, 'myvalue');
--ORA-03113: end-of-file on communication channel
--v_cur_num := dbms_sql.to_cursor_number#myself(v_ref_cur);
v_cur_num := dbms_sql.to_cursor_number(v_ref_cur);
--Compilation error: PLS-00306:
-- wrong number or types of arguments in call to 'DESCRIBE_COLUMNS3'
--dbms_sql.describe_columns3#myself(v_cur_num, v_col_count, v_col_table);
dbms_sql.describe_columns3(v_cur_num, v_col_count, v_col_table);
return v_col_count;
end;
/
begin
dbms_output.put_line('Number of columns: '||count_columns#myself());
end;
/
Number of columns: 1

Cannot call oracle stored procedure and function

Might be too simple question to ask, but I do need help.
I am creating a stored procedure in Oracle 10g, but I cannot call it. I am using SQL Developer to manage the database.
CREATE OR REPLACE
FUNCTION check_login
(username IN VARCHAR2, pwd IN VARCHAR2)
RETURN VARCHAR2
IS
isUserValid INTEGER;
BEGIN
SELECT Count(*) INTO isUserValid
FROM users
WHERE Username = username AND PASS_WORD = pwd;
return isUserValid;
END;
I have tried this also:
CREATE OR REPLACE
PROCEDURE check_login
(username IN VARCHAR2, pwd IN VARCHAR2, RESULT OUT INTEGER)
IS
isUserValid INTEGER;
BEGIN
SELECT Count(*) INTO isUserValid
FROM users
WHERE Username = username AND PASS_WORD = pwd;
RESULT := isUserValid;
END;
Parsing both does not give any error message. I used following syntax to call them:
BEGIN
check_login('admin', 'admin');
END;
AND
EXECUTE check_login('admin', 'admin');
I get this error message....
PLS-00221: 'CHECK_LOGIN' is not a procedure or is undefined
PL/SQL: Statement ignored
The SELECT statement inside both works fine if run directly.
Am I doing something wrong?
If you want to execute a function you have to collect the return value into a variable.
So you need to define a variable and execute function to return into the variable as below
and run it using the run Script option not the Run Statement option.
variable ret varchar2(20);
execute :ret:=check_login(dd,dd);
select :ret from dual
Or if you do it from plsql
declare v_ret varchar2(100);
begin
v_ret:=check_login(a,b);
end;
I find the easiest way to call a function is just selecting the function from dual eg-
select check_login('admin', 'admin') from dual;
Note the error message said "No procedure' :).

Resources