In PL/SQL, can I pass the table schema of a cursor FROM clause via a stored procedure parameter? - oracle

In PL/SQL, I would like to pass in a "source" schema as a parameter to a stored procedure. For instance:
BEGIN
CURSOR my_cursor IS
SELECT my_field FROM <schema>.my_table
...
I want the 'schema' value to come from an input parameter into the stored procedure. Does anyone know how I could do that?
P.S. Sorry if this is a stupid simple question, but I'm new to PL/SQL and must get some functions written quickly.

In addition to what Mark Brady said, another dynamic SQL option is to use a REF CURSOR. Since your sample code includes a cursor this would be the most relevant.
PROCEDURE select_from_schema( the_schema VARCHAR2)
IS
TYPE my_cursor_type IS REF CURSOR;
my_cursor my_cursor_type;
BEGIN
OPEN my_cursor FOR 'SELECT my_field FROM '||the_schema||'.my_table';
-- Do your FETCHes just as with a normal cursor
CLOSE my_cursor;
END;

This has to be done with dynamic sql.
Either the DBMS_SQL package or the Execute Immediate statement.
You can't use variables in the FROM clause.
A potential solution may be to
ALTER SESSION SET Current_Schema = '' <-- the schema you want.
That command changes the default schema. SO if you have a bunch of identically named tables you can save yourself dynamic SQL and make a Dynamic Alter Session.

Related

Pass a regular CURSOR to a PL/SQL procedure that expects SYS_REFCURSOR

I have a lot of named cursors in PL/SQL like this:
cursor MY_CURSOR_01 is select * from my_table_01;
cursor MY_CURSOR_02 is select * from my_table_02;
I want to use them in dbms_xmlgen.newContext procedure which expects a SYS_REFCURSOR or a VARCHAR2 containing the actual query.
I already know that I could do:
dbms_xmlgen.newContect('select * from my_table_01');
But I would like to reuse the existing cursors I have, without rewriting them as string queries.
Any ideas? I am on Oracle 10gR2.
I don't think such a function exists, because Oracle's language specification doesn't allow for cursor objects to be passed as parameters. Unless Oracle has used some magic somewhere, there's just no way to generically reference a cursor without using a refcursor.
However, you don't need to embed your SQL in a string to use a sys_refcursor. Here's a very simple example showing that a sys_refcursor can be opened using static SQL:
DECLARE
c SYS_REFCURSOR;
BEGIN
OPEN c FOR SELECT * FROM DUAL;
CLOSE c;
END;

DDL statements in PL/SQL?

I am trying the code below to create a table in PL/SQL:
DECLARE
V_NAME VARCHAR2(20);
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE TEMP(NAME VARCHAR(20))';
EXECUTE IMMEDIATE 'INSERT INTO TEMP VALUES(''XYZ'')';
SELECT NAME INTO V_NAME FROM TEMP;
END;
/
The SELECT statement fails with this error:
PL/SQL: ORA-00942: table or view does not exist
Is it possible to CREATE, INSERT and SELECT all in a single PL/SQL Block one after other?
I assume you're doing something like the following:
declare
v_temp varchar2(20);
begin
execute immediate 'create table temp(name varchar(20))';
execute immediate 'insert into temp values(''XYZ'')';
select name into v_name from temp;
end;
At compile time the table, TEMP, does not exist. It hasn't been created yet. As it doesn't exist you can't select from it; you therefore also have to do the SELECT dynamically. There isn't actually any need to do a SELECT in this particular situation though you can use the returning into syntax.
declare
v_temp varchar2(20)
begin
execute immediate 'create table temp(name varchar2(20))';
execute immediate 'insert into temp
values(''XYZ'')
returning name into :1'
returning into v_temp;
end;
However, needing to dynamically create tables is normally an indication of a badly designed schema. It shouldn't really be necessary.
I can recommend René Nyffenegger's post "Why is dynamic SQL bad?" for reasons why you should avoid dynamic SQL, if at all possible, from a performance standpoint. Please also be aware that you are much more open to SQL injection and should use bind variables and DBMS_ASSERT to help guard against it.
If you run the program multiple time you will get an error even after modifying the program to run the select statement as dynamic SQL or using a returning into clause.
Because when you run the program first time it will create the table without any issue but when you run it next time as the table already created first time and you don't have a drop statement it will cause an error: "Table already exists in the Database".
So my suggestion is before creating a table in a pl/sql program always check if there is any table with the same name already exists in the database or not. You can do this check using a Data dictionary views /system tables which store the metadata depending on your database type.
For Example in Oracle you can use following views to decide if a tables needs to be created or not:
DBA_TABLES ,
ALL_TABLES,
USER_TABLES

Creating table before creating a cursor in Oracle

I have a PL/SQL procedure which creates a temporary table and then extracts the data from this temporary table using cursors, processes the data and then drops the temporary table. However Oracle doesn't allow the usage of cursor if the table doesn't exist in the database.
Please help me handle this.
Your statement is not quite correct. You can use a cursor for pretty much arbitrary queries. See below:
create or replace procedure fooproc
IS
type acursor is ref cursor;
mycur acursor;
mydate date;
BEGIN
execute immediate 'create global temporary table footmp (bar date) on commit delete rows';
execute immediate 'insert into footmp values (SYSDATE)';
open mycur for 'select * from footmp';
loop
fetch mycur into mydate;
exit when mycur%notfound;
dbms_output.put_line(mydate);
end loop;
close mycur;
execute immediate 'drop table footmp';
END fooproc;
/
(More details here - especially this short proc is not safe at all since the table name is fixed and not session-dependent).
It is (quite) a bit ugly, and I'm not suggesting you use that - rather, you should be thinking whether you need that procedure-specific temporary table at all.
See this other article:
DO NOT dynamically create them [temp tables], DO NOT dynamically create them, please -- do NOT dynamically create them.
Couldn't you use a global temporary table? Do you actually need a temporary table at all? (i.e. doesn't using a cursor on the select statement you'd use to fill that table work?)
Or, if you wish to avoid differences between global temporary tables and "regular" permanent tables you may be used to (see Oracle docs on temp table data availability, lifetime etc), simply create the table first (nologging). Assuming nobody else is using this table, your procedure could truncate before/after your processing.

Getting results in a result set from dynamic SQL in Oracle

This question is similar to a couple others I have found on StackOverflow, but the differences are signficant enough to me to warrant a new question, so here it is:
I want to obtain a result set from dynamic SQL in Oracle and then display it as a result set in a SqlDeveloper-like tool, just as if I had executed the dynamic SQL statement directly. This is straightforward in SQL Server, so to be concrete, here is an example from SQL Server that returns a result set in SQL Server Management Studio or Query Explorer:
EXEC sp_executesql N'select * from countries'
Or more properly:
DECLARE #stmt nvarchar(100)
SET #stmt = N'select * from countries'
EXEC sp_executesql #stmt
The question "How to return a resultset / cursor from a Oracle PL/SQL anonymous block that executes Dynamic SQL?" addresses the first half of the problem--executing dynamic SQL into a cursor. The question "How to make Oracle procedure return result sets" provides a similar answer. Web search has revealed many variations of the same theme, all addressing just the first half of my question. I found this post explaining how to do it in SqlDeveloper, but that uses a bit of functionality of SqlDeveloper. I am actually using a custom query tool so I need the solution to be self-contained in the SQL code. This custom query tool similarly does not have the capability to show output of print (dbms_output.put_line) statements; it only displays result sets. Here is yet one more possible avenue using 'execute immediate...bulk collect', but this example again renders the results with a loop of dbms_output.put_line statements. This link attempts to address the topic but the question never quite got answered there either.
Assuming this is possible, I will add one more condition: I would like to do this without having to define a function or procedure (due to limited DB permissions). That is, I would like to execute a self-contained PL/SQL block containing dynamic SQL and return a result set in SqlDeveloper or a similar tool.
So to summarize:
I want to execute an arbitrary SQL statement (hence dynamic SQL).
The platform is Oracle.
The solution must be a PL/SQL block with no procedures or functions.
The output must be generated as a canonical result set; no print statements.
The output must render as a result set in SqlDeveloper without using any SqlDeveloper special functionality.
Any suggestions?
The closest thing I could think of is to create a dynamic view for which permission is required. This will certainly involve using a PL/SQL block and a SQL query and no procedure/function. But, any dynamic query can be converted and viewed from the Result Grid as it's going to be run as a select query.
DEFINE view_name = 'my_results_view';
SET FEEDBACK OFF
SET ECHO OFF
DECLARE
l_view_name VARCHAR2(40) := '&view_name';
l_query VARCHAR2(4000) := 'SELECT 1+level as id,
''TEXT''||level as text FROM DUAL ';
l_where_clause VARCHAR2(4000):=
' WHERE TRUNC(1.0) = 1 CONNECT BY LEVEL < 10';
BEGIN
EXECUTE IMMEDIATE 'CREATE OR REPLACE VIEW '
|| l_view_name
|| ' AS '
|| l_query
|| l_where_clause;
END;
/
select * from &view_name;
You seem to be asking for a chunk of PL/SQL code that will take an arbitrary query returning result set of undetermined structure and 'forward/restructure' that result set in some way such that is can easily be rendered by some "custom GUI tool".
If so, look into the DBMS_SQL for dynamic SQL. It has a DESCRIBE_COLUMNS procedure which returns the columns from a dynamic SELECT statement. The steps you would need are,
Parse the statement
Describe the result set (column names and data types)
Fetch each row, and for each column, call the datatype dependent function to return that value into a local variable
Place those local variables into a defined structure to return to the calling environment (eg consistent column names [such as col_1, col_2] probably all of VARCHAR2)
As an alternative, you could try building the query into an XMLFOREST statement, and parse the results out of the XML.
Added :
Unlike SQL Server, an Oracle PL/SQL call will not 'naturally' return a single result set. It can open up one or more ref cursors and pass them back to the client. It then becomes the client's responsibility to fetch records and columns from those ref cursors. If your client doesn't/can't deal with that, then you cannot use a PL/SQL call.
A stored function can return a pre-defined collection type, which can allow you to do something like "select * from table(func_name('select * from countries'))". However the function cannot do DML (update/delete/insert/merge) because it blows away any concept of consistency for that query. Plus the structure being returned is fixed so that
select * from table(func_name('select * from countries'))
must return the same set of columns (column names and data types) as
select * from table(func_name('select * from persons'))
It is possible, using DBMS_SQL or XMLFOREST, for such a function to take a dynamic query and restructure it into a pre-defined set of columns (col_1, col_2, etc) so that it can be returned in a consistent manner. But I can't see what the point of it would be.
Try try these.
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
declare
v_rc sys_refcursor;
begin
v_rc := get_dept_emps(10); -- This returns an open cursor
dbms_output.put_line('Rows: '||v_rc%ROWCOUNT);
close v_rc;
end;
Find more examples here. http://forums.oracle.com/forums/thread.jspa?threadID=886365&tstart=0
In TOAD when executing the script below you will be prompted for the type of v_result. From the pick list of types select cursor, the results are then displayed in Toad's data grid (the excel spreadsheet like result). That said, when working with cursors as results you should always write two programs (the client and the server). In this case 'TOAD' will be the client.
DECLARE
v_result sys_refcursor;
v_dynamic_sql VARCHAR2 (4000);
BEGIN
v_dynamic_sql := 'SELECT * FROM user_objects where ' || ' 1 = 1';
OPEN :v_result FOR (v_dynamic_sql);
END;
There may be a similar mechanism in Oracle's SQL Developer to prompt for the binding as well.

Dynamic PL/SQL

In PL/SQL,I would like to pass a source as well as the target schema as a parameter to a stored procedure. For source we can use:
PROCEDURE select_from_schema( the_schema VARCHAR2)
IS
TYPE my_cursor_type IS REF CURSOR;
my_cursor my_cursor_type;
BEGIN
OPEN my_cursor FOR 'SELECT my_field FROM '||the_schema||'.my_table';
-- Do your FETCHes just as with a normal cursor
CLOSE my_cursor;
END;
For the target insert or update statement, how can we use that schema inside that insert or update statement....Does anyone know how could I do that???
P.S. Excuse me; I am a beginner and must get some functions written quickly.
You can do the same thing for an INSERT or UPDATE that you did for a SELECT - use dynamic SQL like this:
EXECUTE IMMEDIATE 'INSERT INTO '||target_schema||'.my_table (col1,col2...) VALUES(:val1, :val2...)' USING my_row.col1, my_row.col2...;

Resources