Querying multiple rows from Oracle table using package - sql

I wrote a package to query rows from a table. This select query will call other functions and returns all the rows from table. But when i write a package with all functions and sprocs , my sproc with select statement gives me an error saying i cannot execute without into statement. But if i use into then it will return only one row. How can i retrieve all rows using oracle sp?
Procedure GetData As
BEGIN
Select Jobid, JobName, JobLocation, JobCompany, X(jobid) FROM jobsTable; END GetData;
END;
I had to change it to following make the error go away:
Procedure GetData As
r_Jobid jobsTable.jobid%type;
r_JobName jobsTable.jobName%type;
r_JobLocation jobsTable.jobLocation%type;
r_temp varhar2(10);
BEGIN
Select Jobid, JobName, JobLocation, JobCompany, X(jobid)
INTO r_jobid, r_jobName, r_jobLocation, r_temp
FROM jobsTable;
END GetData;
END;

This is a better approach to returning multiple rows from a function:
FUNCTION GET_DATA()
RETURN SYS_REFCURSOR IS
results_cursor SYS_REFCURSOR;
BEGIN
OPEN results_cursor FOR
SELECT t.jobid,
t.jobName,
t.joblocation,
t.jobcompany,
X(t.jobid)
FROM JOBSTABLE t;
RETURN results_cursor;
END;
I agree with afk though that this doesn't appear to be what you really need to be using. Here's my recommendation for using a cursor:
CURSOR jobs IS
SELECT t.jobid,
t.jobName,
t.joblocation,
t.jobcompany,
X(t.jobid)
FROM JOBSTABLE t;
v_row jobs%ROWTYPE; --has to be declared AFTER the cursor to be able to reference the row type
BEGIN
OPEN jobs;
FETCH jobs INTO v_row;
IF jobs%FOUND THEN
--do stuff here, per row basis
--access columns in the row using: v_row.jobid/etc
END IF;
CLOSE jobs;
END;
Are you aware that this:
Procedure GetData As
r_Jobid jobsTable.jobid%type;
r_JobName jobsTable.jobName%type;
r_JobLocation jobsTable.jobLocation%type;
r_temp varhar2(10);
...means you defined local variables? You won't be able to get information out of the procedure. If you do, you'd need parameters, like this:
Procedure GetData(IO_R_JOBID IN OUT JOBSTABLE.JOBID%TYPE,
IO_R_JOBNAME IN OUT JOBSTABLE.JOBNAME%TYPE,
IO_R_JOBLOCATION IN OUT JOBSTABLE.JOBLOCATION%TYPE,
IO_R_TEMP IN OUT VARCHAR2(10)) AS
I use the IO_ to note which parameters are IN/OUT. I'd use IN_ or OUT_ where applicable. But the key here is to define OUT if you want to get a parameter back out.
Also - packages are just logical grouping of procedures & functions, with the ability to define constants scoped to the package. The package itself doesn't execute any SQL - it's still a function or procedure that is executing. God how I wish SQL Server had packages...

You could use a pipelined function. For this example I'm only fetching the ID columns, but you just need to add the others.
CREATE PACKAGE jobsPkg AS
TYPE jobsDataRec IS RECORD ( jobid jobsTable.jobid%type );
TYPE jobsDataTab IS TABLE OF jobsDataRec;
FUNCTION getJobsData RETURN jobsDataTab PIPELINED;
END jobsPkg;
/
CREATE PACKAGE BODY jobsPkg AS
FUNCTION getJobsData RETURN jobsDataTab PIPELINED
IS
output_record jobsDataRec;
BEGIN
FOR input_record IN ( SELECT jobid FROM jobsTable ) LOOP
output_record.jobid := input_record.jobid;
PIPE ROW (output_record);
END LOOP;
END getJobsData;
END jobsPkg;
/
This function can then be used as a row source:
SELECT * FROM TABLE( jobsPkg.getJobsData );

You need to use a Cursor for multiple return results. Take a look at this article on using Cursors for some details.
You could do something like:
DECLARE
CURSOR myCur IS
SELECT Jobid, JobName, JobLocation, JobCompany, X(jobid)
FROM jobsTable;
BEGIN
FOR resultRow in myCur
LOOP
--do your stuff here
END LOOP;
END;

Related

How to return a set of results from one Oracle subprogram to another?

This is a simplified version of what I'm trying to do, but let's say I want to select all employees whose manager is in a given department. I could have a SQL query like the following:
SELECT employees.id
FROM employees
WHERE employees.manager IN (SELECT managers.id
FROM managers
WHERE managers.dept = 12)
;
But let's say I want to abstract the manager subquery into a PL/SQL subprogram. How do I do that?
The stored procedures I've worked with (which are mostly written by other developers) tend to have out parameters that get mapped by PHP calling code into a PHP array. I don't really have any experience of calling one stored procedure from another.
What I'd like to do is to have something like this:
SELECT employees.id
FROM employees
WHERE employees.manager IN my_stored_procedure(12)
;
and then my_stored_procedure would output the set of manager IDs for the input parameter (which is 12 in this example).
It is not possible to do exactly as you have posted, but if the selection of managers are not straightforward, you could abstract it through a view or make use of a function that returns a table, like this:
SELECT employees.id
FROM employees
WHERE employees.manager IN (SELECT * from TABLE(get_managers_from_dept(12)));
In this link there is an example of that approach:
Function or Procedure for an IN clause
To Call A stored Proc from another Stored Proc you just need to call it from the Main Proc as mentioned below. This Main Proc can be called /initiated by a PHP Code.
PROCEDURE some_sp
AS
BEGIN
some_other_sp('parm1');
END;
Although less straight-forward, You can do accomplish it by using dynamic sql. This is the structure of your stored procedure. It returns a comma separated list of manager_ids for a given department.
CREATE OR REPLACE FUNCTION my_stored_procedure(
p_dept NUMBER)
RETURN VARCHAR2
IS
v_manager_list VARCHAR2(1000);
BEGIN
SELECT m.id INTO v_manager_list FROM managers m WHERE m.dept = p_dept;
RETURN '('||v_manager_list||')';
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN '(NULL)';
END;
/
Now you cannot use this to directly query as ...IN my_stored_procedure(12),
rather you must use a dynamic fetch into a collection.
SET SERVEROUTPUT ON;
DECLARE
TYPE v_emp_type
IS
TABLE OF employees.id%TYPE;
v_emp v_emp_type;
BEGIN
EXECUTE IMMEDIATE 'SELECT employees.id
FROM employees
WHERE employees.id IN '|| my_stored_procedure(100) BULK COLLECT INTO v_emp ;
FOR i IN v_emp.FIRST..v_emp.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(v_emp(i));
END LOOP;
END;
/

Syntax for SQL function, in variables and return

I have a sql function called task2.sql:
-- script to create function task2
CREATE OR REPLACE Function task2
(input_userid IN integer)
begin
return (select *
from tweet twt
where twt.userid = input_userid
order by publishtime desc, tweetid desc);
END;
/
exit;
I'm trying to return all the rows with the goal being to write them to a text file. I'm using a java file to call this function and to manage the results. I am messing up the syntax somewhere. Any help is appreciated.
Oracle functions can't do this in such way. For returning rows from function you should use Table Function (with pipeline may be)
http://docs.oracle.com/cd/B19306_01/appdev.102/b14289/dcitblfns.htm#CHDCIEJG
But, for wrapping query into procedure i can reccomend you return SYS_REFCURSOR
CREATE OR REPLACE PROCEDURE task2
(input_userid IN integer, out_cur OUT SYS_REFCURSOR)
begin
OPEN out_cur FOR select *
from tweet twt
where twt.userid = input_userid
order by publishtime desc, tweetid desc;
END;
Then, you should fetch rows from out_cur in your application. It's easy and google and SF full of this examples. For example read data from SYS_REFCURSOR in a Oracle stored procedure and reuse it in java

PL/SQL stored function return with row type

CREATE OR REPLACE FUNCTION lab( f_name IN VARCHAR2 )
RETURN test%ROWTYPE
IS
total NUMBER := 0;
CURSOR c_app IS
SELECT count(*),LISTAGG(s.sname,',') WITHIN GROUP (ORDER BY s.sname)
FROM APPLICANT a INNER JOIN SPOSSESSED s ON a.A# = s.A#
WHERE a.fname = f_name;
rec_app c_app%ROWTYPE;
BEGIN
OPEN c_app;
LOOP
FETCH c_app into rec_app;
EXIT WHEN c_app%NOTFOUND;
END LOOP;
CLOSE c_app;
RETURN rec_app;
END lab;
/
Fail to compile with errors that expression wrong type?
Isn't it possible to return with rowtype result?
for example i run this function
select lab(fname) from position where fname='PETER';
so the result will be display like
PETER : aaaa,bbbb,cccc
You're declaring the return as test%rowtype, then trying to return rec_app, which is declared as c_app%rowtype - so the types don't match. You can't do that.
c_app is only in scope within this function so it would not have any meaning for any callers, and you can't use it as the return type. You can return something that is actually test%rowtype, assuming test is a table, but not an arbitrary different type. It isn't clear that there is any relationship at all between your cursor and its row type, and the test table.
You're also looping round to potentially fetch multiple rows, but only returning the last one (or trying to, anyway), which probably isn't what you mean to do.
The simplest way to get all the cursor rows back to the caller is with a ref cursor:
CREATE OR REPLACE FUNCTION lab( f_name IN VARCHAR2 )
RETURN SYS_REFCURSOR
IS
ref_cur SYS_REFCURSOR;
BEGIN
OPEN ref_cur FOR
SELECT count(*),LISTAGG(s.sname,',') WITHIN GROUP (ORDER BY s.sname)
FROM APPLICANT a INNER JOIN SPOSSESSED s ON a.A# = s.A#
WHERE a.fname = f_name;
RETURN ref_cur;
END lab;
/
If you create an external type you could use PIPELINED but that doesn't appear necessary here. neither is quite using a %rowtype though. You can only return a %rowtype if you have a table that has the columns you want to return.

Possible to extend static SQL statements with dynamic parts?

I'd like to create a Oracle package where I have a procedure that executes some dynamic SQL. This is no problem if I'm doing it all dynamic with EXECUTE IMMEDIATE but it would be better if the static parts of the query could be coded static (to have compile time checking).
Example of fully dynamic query:
-- v_stmt is built dynamically.
v_stmt := 'SELECT count(*) FROM <here some joins> WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
USING v_param1, v_param2
RETURNING INTO v_count;
Example of what I tried to make the FROM-part static:
-- v_stmt is built dynamically.
v_stmt := 'SELECT count(*) FROM my_package.my_function(:param1, :param2) WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
USING v_param1, v_param2
RETURNING INTO v_count;
FUNCTION my_function(
i_param1 IN VARCHAR2,
i_param2 IN NUMBER
)
RETURN SYS_REFCURSOR
AS
v_cursor SYS_REFCURSOR;
BEGIN
-- Open a cursor for different queries depending on params.
IF i_param2 = 1 THEN
OPEN v_cursor FOR <some static query>;
ELSE
OPEN v_cursor FOR <some other static query>;
END IF;
RETURN v_cursor;
END;
This doesn't work because it's not possible to select from a SYS_REFCURSOR (at least that's what I found with Google).
Is there any way to reach this goal?
edit: As requested, here are some examples:
Static queries:
SELECT a.*, ca.CUS_ID FROM adresses a INNER JOIN customer_adresses ca ON (ca.adr_id = a.adr_id);
SELECT p.*, cp.CUS_ID FROM persons p INNER JOIN customer_persons cp ON (cp.per_id = p.per_id);
Then they are extended dynamically like the following examples:
-- Checks if there is an adress in the customer where the zip is null.
SELECT count(*) FROM <static adresses query> q WHERE q.cus_id = :param1 AND a.zip IS NULL;
-- Checks if there is at least one person in the customer.
SELECT count(*) FROM <static persons query> q WHERE q.cus_id = :param1;
Sorry, but why the need to do this? Seems you're over complicating things by introducing a function that will return different types of data/tables depending on the parameter list. Very confusing imo. Besides, you have to do the work somewhere, you're just trying to hide it in this function (inside if param1=this then x if param1=that then y...)
Besides, even if you did implement a cursor function (even pipelined), it would be a bad idea in this case because you'd be forcing Oracle into doing work that it wouldn't necessarily need to do (ignore all the context switching for now). To just get a count, you'd have Oracle grab each an every row result and then count. Many times Oracle can just do a fast full index scan to get the count (depending on the query of course). And often same query run multiple times will not need to do all the work each time if blocks are found in buffer cache. I'd challenge you to run the count multiple times using straight SQL vs using a function returning a cursor. You might be surprised. And to my knowledge (check me on this) the new 11g function result cache won't work on a pipelined functions or a function returning a ref cursor (along with other issues like invalidations due to relies on tables).
So, what I'm saying is why not just do: select count(1) into v_variable from ...;
If you want to hide and modularize, then just know what you're potentially losing.
You may want to open a query in function1 and then pipeline the results of it as a table to function2 which then will add a where clause to this "table"
In this case you'll want to rewrite your function1 as a pipelined table function
v_stmt := 'SELECT count(*) FROM table(my_package.my_function(:param1, :param2)) WHERE <here some conditions>';
EXECUTE IMMEDIATE v_stmt
USING v_param1, v_param2
RETURNING INTO v_count;
CREATE TYPE object_row_type AS OBJECT (
OWNER VARCHAR2(30),
OBJECT_TYPE VARCHAR2(18),
OBJECT_NAME VARCHAR2(30),
STATUS VARCHAR2(7)
);
CREATE TYPE object_table_type AS TABLE OF object_row_type;
FUNCTION my_function(
i_param1 IN VARCHAR2,
i_param2 IN NUMBER
)
RETURN object_table_type PIPELINED AS
BEGIN
You can have compile time checking of expressions with Oracle expression filter.
It's probably more complicated than the other solutions, but if you really need to verify your conditions it can be helpful.

Viewing query results with a parameters in Oracle

I need to run big queries (that was a part of SP) and look at their results (just trying to find a bug in a big SP with many unions. I want to break it into parts and run them separately).
How can I do that if this SP have few parameters? I don't want to replace them in code, it would be great just to add declare in a header with a hardcode for this parameter.
I've tried something like this:
DECLARE
p_asOfDate DATE := '22-Feb-2011';
BEGIN
SELECT * from myTable where dateInTable < p_asOfDate;
END
But it says that I should use INTO keyword. How can I view this results in my IDE? (I'm using Aqua data studio)
I need to do that very often, so will be very happy if will find a simple solution
You are using an anonymous block of pl/sql code.
In pl/sql procedures you need to specify a target variable for the result.
So you first need to define a variable to hold the result in the declare section
and then insert the result data into it.
DECLARE
p_asOfDate DATE := '22-Feb-2011';
p_result myTable%ROWTYPE;
BEGIN
select * into p_result from myTable where dateInTable < p_asOfDate;
END
That said you will probaply get more than one row returned, so I would use
a cursor to get the rows separately.
DECLARE
CURSOR c_cursor (asOfDate IN DATE) is
select * from myTable where dateInTable < asOfDate;
p_asOfDate DATE := '22-Feb-2011';
p_result myTable%ROWTYPE;
BEGIN
OPEN c_cursor(p_asOfDate);
loop
FETCH c_cursor into p_result;
exit when c_cursor%NOTFOUND;
/* do something with the result row here */
end loop;
CLOSE c_cursor;
END
To output the results you can use something like this for example:
dbms_output.put_line('some text' || p_result.someColumn);
Alternatively you can execute the query on an sql command-line (like sqlplus)
and get the result as a table immediately.
I hope I understood your question correctly...
update
Here is a different way to inject your test data:
Use your tools sql execution environemnt to submit your sql statement directly without a pl/sql block.
Use a "&" in front of the variable part to trigger a prompt for the variable.
select * from myTable where dateInTable < &p_asOfDate;
The Result should be displayed in a formatted way by your tool this way.
I do not know about Aqua, but some tools have functions to define those parameters outside the sql code.