Write Oracle Procedure To Accept List of Items used in Select - oracle

There have been a couple of hints that seem to have gotten me close here, but with some unique issues, I'm hoping this question is distinguishing enough to merit its own posting.
For starters here's what I have. I have an Oracle procedure that returns a standard REF CURSOR, and this REF CURSOR is passed back to my application. The REF CURSOR is a list of lookup IDs.
I then want to take this list and bring it to another data store and use it in a select statement. It will absolutely be possible to accomplish this by looping through the REF CURSOR, but I'm hoping to avoid that. I would much rather be able to write a SELECT...WHERE lookup_id IN result_ref_cursor OR SELECT...WHERE EXISTS...
First is this possible or should I just try a less than elegant solution? If it is possible, any hints as to where I should get started looking?
I'm relatively new to Oracle, but fairly experienced in RDBMs in general, so feel free to just through some links at me and I can study up. Much appreciated

Why kurosch didn't put his response as an "answer" I'll have no idea.
So, what you do is define a SQL type which describes one row of the output of the ref cursor, and also a SQL type which is a table of the previous. Then, you'll create a pipelined function which returns the rows returned by the ref cursor. This function can then be used in a standard SQL. I'm borrowing from Ask Tom on this one.
create or replace type myLookupId as object ( id int)
/
create or replace type myLookupIdTable as table of myLookupId
/
create or replace function f return myLookupIdTable PIPELINED is
l_data myLookupId;
l_id number;
p_cursor SYS_REFCURSOR;
begin
p_cursor := function_returning_ref_cursor();
loop
fetch p_cursor into l_id;
exit when p_cursor%notfound;
l_data := myLookupId( l_id );
pipe row (l_data);
end loop;
return;
end;
/
And now a sample query...
SELECT *
FROM SOME_TABLE
WHERE lookup_id in (SELECT ID FROM table(f));
Sorry if the code isn't exactly right, I don't have the DB to test right now.

There are several directions you could go with this, but I did a search on the specific solution you want and it seems like no-one has done it often enough to show up there. What you can do is search the oracle metalink - that is usually really good at finding obscure answers. (Though you do need a service agreement - just found out that mine expired :( )
Other possible solutions:
Create a link between the data stores so that you can do the select in the plsql directly
Create a function in Java that loops through it for you to create the string for the query. This will look a little more pretty at least.
Otherwise, REF CURSOR's need to go back and forth - I don't know how you can pipe the results of the REF CURSOR in one connection to the query in another without looping through it.

Related

Oracle multiple cursors with the same "WHERE" clause

I need to write a script with an embedded PL/SQL that opens a few cursors and loops through records fetched performing some actions.
All cursors have part of their WHERE a condition that look like this: AND table_name IN ('table_one', 'table_two' ... 'last_table_from_a_long_list')
Because the same list of tables is used everywhere I would prefer not to repeat myself but rather to declare the list of tables somewhere and reuse it in the each cursor definition. Ideally you would put those table names in a real table and everything would work OK; however that script will run in production and creating/dropping tables for this purpose is not an accepted practice. The same restriction would apply about creating a user defined type of table_name and another one as a TABLE OF table_name.
Just wondering if there are some PL/SQL tricks that would help me achieve the same thing. Repeating the long list of tables a dozen of times would make that code look quite bad. This said my script is an "one off" thing so maybe I am worrying too much. There is still the professional "how to" curiosity though.
Thank you in advance for your inputs.
Short answer: repeat the code.
Longer answer: not repeating the code is more work than just biting the bullet and dealing with a little less-than-perfectly elegant code.
Longest answer: if you really need to insist on doing this then look into using dynamically-created SQL - build your SELECT statement(s) out of strings, then either use the OPEN cursor FOR sqlString, FETCH cursor INTO..., and CLOSE cursor statements, or for extra karma and a pile of difficulty study up on the DBMS_SQL package - and may C͡͏ţ͘húl͞ḩ̸u҉͝ have mercy upon your soul.
Best of luck.
Couple of options you can try
EXECUTE IMMEDIATE
-- Cursor Example
sql_stmt := 'SELECT * FROM emp WHERE empno = :id';
EXECUTE IMMEDIATE sql_stmt INTO emp_rec USING emp_id;
Another option is DBMS_SQL
CREATE OR REPLACE PROCEDURE DYNSQL AS
cur integer;
rc integer;
BEGIN
cur := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cur, 'CREATE TABLE X (Y DATE)', DBMS_SQL.NATIVE);
rc := DBMS_SQL.EXECUTE(cur);
DBMS_SQL.CLOSE_CURSOR(cur);
END;
You can google with these key words and you will find many examples.

Oracle: Using Procedure or Cursor to store a Select-Statement?

I have a PL/SQL package where i want to declare a select-statment which are used by different other Packages. So i see to ways. First way i define a cursor which can be called from other packages and store the select. Second way would be a procedure which stored the select.
Can someone tell me the advantages and disadvantages of each way? My Prof. say Cursor are old and statefull and noone use this today. My Chef tell me Cursor is faster to iterate and you can make Types of it.
Can someone tell me what's the best practice here?
For example:
CURSOR crs_active_customer IS
SELECT * FROM customer where status = 'active'
OR
PROCEDURE prc_getActiveCustomer IS
BEGIN
SELECT * FROM customer where status = 'active';
END prc_getActiveCustomer;
What is better way to store select-statements.
I would write a function that returns a new cursor instance every time you call it. A cursor variable in a package is actually a GLOBAL variable: you can have only one procedure at a time using it. This is probably the problem your professor is referring to.
Having a global cursor means that you will run into "cursor already open" errors if you write a a procedure that, while scanning the results of such cursor calls another function that internally needs to use the same cursor.
PL/SQL 101 to the rescue ! From Working with Cursors:
The central purpose of the Oracle PL/SQL language is to make it as easy and efficient as possible to query and change the contents of tables in a database. You must, of course, use the SQL language to access tables, and each time you do so, you use a cursor to get the job done.
So every time you have SQL in PL/SQL there will be a cursor. The next question is what kinds of cursors there is and when to use them. The above mentioned article touches also this topic.
You can also read the fine manual: Cursors
A cursor is a pointer to a private SQL area that stores information about processing a specific SELECT or DML statement.
And then carry on reading about implicit and explicit cursors.
Next find a better professor.

construct an MCD (merise) where a table contain columns from the content of another table

i'am facing a problem while trying to make a conception for a school project.and i have two question.
1/
is it possible with oracle to do something like this : https://mariadb.com/kb/en/dynamic-columns/
2/
how can i make a modelisation for that with an MCD (method merise ).
thanks
Quote from your link: "It works by storing a set of columns in a blob and having a small set of functions to manipulate it.". I don't see an obvious reason why you couldn't write something similar for Oracle (or any other relational database).
Alternatively, take a look at EAV, which is a more traditional solution for dynamic columns.
Sorry, I'm not familiar with Merise.
1/ Oracle is not made for creating dynamic columns, but you can do it by using PL/SQL functions (but not while querying with SQL):
create or replace procedure add_table_column(table_name varchar2, column_name varchar2, column_type varchar2)
is
v_script varchar2(4000);
begin
v_script := 'alter table '||table_name||' add '||column_name||' '||column_type;
execute immediate v_script;
end;
/
You can call it this way:
begin
add_table_column('toto','test','number');
end;
/
2/ Merise method is not made for "dynamic objects" as you must begin with a complete data dictionary.
How is your object dynamic, and why do you have to add columns?
You can always get a solution to work by using "regular" tables and relationships.
It sounds like you should start by analyzing the technical implementation in mind. MERISE says to analyze your data model first and start to think about technical solutions after.

ORACLE return from function without creating a new type

I'm wanting to create a function (oracle 11g) that will return multiple values but without having to creating a new TYPE. The end goal is much more complicated than the example I'm providing, but I can do pretty much everything else except get the values to return.
Here is an extremely simplified version of what I'm having trouble with.
for example given the following table (employees):
emplid | emplname | emplchildren
478 |SAM |"GEORGE,RON"
479 |JOSE |"RICHARD,JANE,RACHEL"
480 |PAM |"JORDAN"
I would like the following statement select CHILD_FN from employees to return:
GEORGE
RON
RIRCHARD
JANE
RACHEL
JORDAN
This is simplified to show the part I'm having trouble with
Here is a sample code:
create or replace function CHILD_FN RETURN employee.emplname%TYPE IS
chldnames employee.emplname%TYPE;
CURSOR child_cur
IS
Select emplchildren FROM employees;
begin
/*do complicated parsing to separate each delimited
value of child_cur and assign
it to a new row in the names_col variable/
/*how do I add values to the names_col variable? I've tried
'chldnames.extend',
various types of 'bulk collect into chldnames'
and they all give various errors*/
return chldnames;
end LOCAL_TEST_FN;
do I need to change my return type, or is it not declared properly?
I'm not completely against created a new type in the database, it's just we've gotten very far without ever having to create a new type, and the less complicated I can make things, the better. And if there is a way around it, I would prefer to learn that way and make the decision on which is better.
Let me know if I need to provide more information.
There are many pre-defined collections that can be useful. For example, I generally use sys.odciVarchar2List, a VARRAY(32767) OF VARCHAR2(4000). But usually only for adhoc code. For production code you're probably better off creating your own types. It will make things more clear and is less likely to cause problems if someone decides to revoke access to standard objects.
create or replace function child_fn return sys.odcivarchar2list is
childnames sys.odcivarchar2list := sys.odcivarchar2list();
begin
--Add elements
childnames.extend;
childnames(childnames.last) := 'GEORGE';
childnames.extend;
childnames(childnames.last) := 'RON';
--...
--Also could use something like this:
--childnames := sys.odciVarchar2List('GEORGE','RON','RIRCHARD','JANE');
return childnames;
end;
/
--For SQL it's often more convenient to use it like a table:
select column_value from table(child_fn);

Quick-n-dirty results: View results of Procedure OUT cursor in SQL Worksheet?

Platform: Oracle
Language: PL/SQL
Issue: Want to output a procedure OUT cursor into the SQLDeveloper SQLWosksheet.
Anyone know how to use the Oracle "Select * from Table( PipelinedFunction( Param ) ) " to check procedure code output cursors?
I am using Crsytal Reports off of an Oracle stored procedure. Crystal requires that a procedure return a cursor, which it fetchs and reads.
The procedure code I have is currently working, but I want to find the easiest way to view the effects of changes to the procedure code. I have SQLDeveloper available, and I'm doing my creation and sql testing in that. I would like to get a quick result visible in the SQL Developer Query Result window ("SQL Worksheet").
Is there a (simple) way to use a Function to read the cursor from the procedure? (and pipe that out to the Table function?)
Convoluted, I know, but I deal best when I can just see the results of code changes. If I can view the record results directly, it will speed up development of the report.
I know of the Table function and a little about pipelining in Oracle. I know a little about cursors in general and sys_refcursor. I know diddly about types and why I need them. (Isn't sys_regCursor supposed to get us away from that?)
The current procedure does an adequate but ungraceful series of queries, inserts to global temp tables (GTT), joins from GTT and original tables, more inserts, and more self-joins and then SELECTS the results into the OUT cursor. I might be able to do better relying on just cursors and such, but the current method is good enough to get results to the report.
I think I can handle SQL pretty well (for our purposes), but I am not an Oracle-specific developer... but I need help.
Anybody run across this? The whole idea was to speed my development for the procedure code, but I've spent a couple of days looking for a way to just get at the output... not what I had in mind.
Update:
I have tried some hare-brained schemes based on slivers that I've seen on the web... such as
Create or replace FUNCTION GET_BACKPLANE (
Node VARCHAR2 ) RETURN SYS_REFCURSOR
AS
RESULTS SYS_REFCURSOR;
BEGIN
Open Results for
Select Backplane(Results, Node) from Dual ;
... etc.
and
Create or replace Function GET_BACKPLANE (
NODE VARCHAR2 ) RETURN My_Table_Stru%ROWTYPE PIPELINED
AS
BEGIN ...
I don't think that Oracle is even considering letting me re-reference the output cursor from the procedure ("Results" is a sys_refcursor that holds the results of the last SELECT in the procedure). I don't know how to define it, open it, and reference it from the procedure.
I never got to the place where I could try
SELECT * FROM TABLE(GET_BACKPLANE( ... etc )
Sorry for any typos and bad Oracle Grammar... it's been a long several days.
SQL Developer allows us to use SQL*Plus commands in the Worksheet. So all you need to do is define a variable to hold the output of the ref cursor.
I may have misinterpreted the actual code you want to run but I'm assuming your actual program is a procedure Backplane(Results, Node) where results is an OUT parameter of datatype sys_refcursor and node is some input parameter.
var rc refcursor
exec Backplane(results=>:rc, Node=>42)
print rc
The output of the print statement is written to the Script Output pane.
Note that the use of SQL*Plus commands means we have to use the Run Script option F5 rather than execute statement.
Thanks for the help. In the end, I wound up brute-force-ing it...
Step by step:
Make a query, test a query,
create a global temp table from the structure,
add code to make another query off of that GTT, test the query,
create a global temp table from the structure,
etc.
In the end, I wound up running (anonymous block) scripts and checking the GTT contents at every stage.
The last part was to use the same last query from the original procedure, stuffing everything into the Cursor that crystal likes...
tomorrow, I test that.
But, I'll just force it through for the next procedure, and get it done in a day and a half instead of 2+ weeks (embarrassed).
Thanks,
Marc

Resources