Table variable in Oracle stored procedure - oracle

I'm new to Oracle and I need to translate my SQL Server stored procedure to oracle. In SQL Server, I have function that returns a table variable.
Executing looks like:
Declare #tblTSVal table(stValue varchar(500), itemKey varchar(255), props varchar(max))
insert #tblTSVal(stValue, itemKey, props)
select * from dbo.fn_ag_valuesToTable(#tsVal)
I write this function in my Oracle database and it's working. But I do not know how to declare a table variable in stored procedure.
Now I'm trying like this:
Declaration:
type AGValues as object
(
stValue varchar(255),
itemKey varchar(255),
props varchar(8000)
);
type ValTBL is Table of AGValues;
Executing:
insert into tblTSVal(stValue, itemKey, props)
select * from TABLE(dbMis.fn_ag_valuesToTable(tsVal));
That's causes an error:
Table or view does not exist.
Give me some advice please.

ValTBL is so-called nested table. To load data in it please use BULK COLLECT option.
Example:
SELECT * BULK COLLECT INTO tblTSVal FROM TABLE(dbMis.fn_ag_valuesToTable(tsVal));
But that is not root of the "Table or view does not exist." error problem. Please check your rights to execute dbMis.fn_ag_valuesToTable function.
To use pl/sql function as a table is worth to read about pipelined and parallel table functions.
http://docs.oracle.com/cd/E11882_01/appdev.112/e10765/pipe_paral_tbl.htm#ADDCI2140

I haven't tried this, but since you're not returning the query results directly as output (ie you're executing a command), what if you wrapped it in a PL/SQL block?
BEGIN
execute immediate 'insert into tblTSVal(stValue, itemKey, props) ' ||
'select * from ' || dbMis.fn_ag_valuesToTable(tsVal);
END;
/
I'm assuming dbMis.fn_ag_valuesToTable is a function.

Related

Oracle WITH AS SELECT with GRANT

I'm using WITH to create like a temporary table and query upon it. But my SELECT(running on the temp table) has a function call with temp table as the input parameter. How to give function access to temp table created using WITH.
WITH TEMP_TABLE AS
(select * from schema1.main_table where col_datetime > sysdate - 4) -- to reduce the data main query executes upon
(
Select * FROM table(schema2.FUNCTION1(blah, blah, 'TEMP_TABLE', blah, blah))
);
Error below
ORA-00942: table or view does not exist
ORA-06512: at "schema2.FUNCTION1", line 143
ORA-06512: at line 1
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
You are using a Common Table Expression (CTE). A CTE is temporarily created only in the context of the broader query and so the CTE's complete dataset of records are outside of the usable scope of functions. You can use individual values from the CTE and pass to a function but your function cannot see the full CTE. The CTE does not exist in a context accessible by the function.
You may consider posting a separate question describing what you hope to accomplish inside the function and we may be able to give you some pointers on an alternate method that fits the SQL specifications.
The PL/SQL WITH function can directly answer your question. You can't pass the WITH table, but you can use a WITH function to create a table, and then reference that table in your existing function.
This code requires Oracle 12.1. And please be aware of the potential for SQL injection with the names.
with
function create_temp_table return varchar2 is
pragma autonomous_transaction;
begin
execute immediate 'drop table temp_table';
execute immediate 'create table temp_table as select 2 a, 3 b from dual';
return 'temp_table';
end;
select * from table(function1(create_temp_table()))
/
Here's the sample schema to make the above SQL work:
create or replace type function1_rec is object(a number, b number);
create or replace type function1_nt is table of function1_rec;
create or replace function function1(p_table_name varchar2) return function1_nt is
v_results function1_nt;
begin
execute immediate 'select function1_rec(a,b) from '||p_table_name
bulk collect into v_results;
return v_results;
end;
/
Although I agree with Sam M that you might want to look into alternative methods. I have a feeling there is a simpler way to accomplish what you're looking for.

execute query defined in column as String

Hello again I need some help,
I have table where in column "query" is defined query statement. I would like to run it and as output get the result.For example:
Create table table1
(
ID Number,
Query Varchar2(400)
)
insert into table1(id,query) values (1,'select name from table2 where table2.id=table1.id and table2.type = variable');
create table table2
(ID number,
Name varchar2(400),
Type Varchar2(400)
)
insert into table2 values (1,'Mathew','M');
insert into table2 values (1,'Thomas','G');
insert into table2 values (2,'Jerry','P');
For now query :
'select name from table2 where table2.id=table1.id and table2.type = variable'
should return only "Mathew" (assuming variable as 'M' - procedure variable input)
As procedure input I want to have variable which I will replace somehow in query statement.
Could you give me some tips how to handle with that?
------------Edit
I did stmh like that:
create or replace procedure queryrun
(var1 varchar2) as
str VARCHAR2(200);
BEGIN
execute immediate 'select replace(query,''variable'','''||var1||''') from table1' into str;
dbms_output.put_line('Value is '||str);
END;
But as result it present query... no result of select statement...
You are only selecting your query, not running it; and you're replacing the string "'variable'" - including the single quotes - with your value, but your original query string doesn't have the single quotes around it - so nothing matches.
You should not really substitue a hard-coded value anyway. Change your stored query to include a bind variable placeholder instead:
insert into table1(id,query)
values (1,'select name from table2 where table2.id=table1.id and table2.type = :variable');
Although that query is invalid anyway - you don't have table1 defined in the from clause or a join clause. When you have a valid query you can run standalone, use that, but with a bind variable (denoted by the leading colon).
But let's assume you have a valid query string in your table, that will only return one row, say:
insert into table1(id,query)
values (1,'select name from table2 where type = :variable');
Your procedure then needs a local variable to hold that query string. You select your query into that using static SQL, and then use dynamic SQL via execute immediate to run the query from that string, and provide the bind value with the using clause. The result goes into another local variable, which you are already doing.
So a simple version might look like this:
create or replace procedure queryrun (p_var1 varchar2) as
l_query table1.query%type;
l_name table2.name%type;
begin
select query into l_query from table1 where id = 1;
execute immediate query into l_name using p_var1;
dbms_output.put_line('Value is ' || l_name);
end;
This is obviously rather contrived. If you have multiple queries in your table, and perhaps pass a second ID variable into the procedure to choose which one to run, they would all have to take a single bind variable, and would have to all be able to put the result into the same type and size of result variable. You're also restricted to queries that return exactly one row. You can adapt and extend this of course, but hopefully this will get you started.
You can have bind variable and use plsql execute immediate.
Examples:
http://www.dba-oracle.com/t_oracle_execute_immediate.htm

Oracle equivalent of stored procedure that returns an inline table?

Example in T-SQL (SQL Server - taken from here):
CREATE PROC proc_authors
#au_lname VARCHAR(40)
AS
SELECT
au_id, au_fname, au_lname, city, state
FROM authors
WHERE au_lname = #au_lname
go
Is it possible in Oracle to create a stored procedure that returns an inline table (without declaring a type - like the above)? If not, what would be the closest alternative? i.e. declare inline type, then use it. The idea is to minimize number of DB permissions that are granted.
Please include sample code as part of your answer.
Reasoning behind using stored procedure vs function - we have legacy software that can only execute stored procedures, or raw queries. It appears that only stored procedures in there have support for parameterized execution, which is what we are after.
try this with ref cursor
PROCEDURE proc_get_tada(ip_user IN VARCHAR2,
op_error_code OUT NUMBER,
op_cursor OUT SYS_REFCURSOR,) AS
BEGIN
OPEN op_cursor FOR
SELECT * FROM your_table yt where yt.user = ip_user;
EXCEPTION
WHEN OTHERS THEN
op_error_code := -1;
END proc_get_tada;
you will get collection of all data from you table you can iterate in java or calling program.
Maybe you are searching for something like this:
create table author
(
au_id number,
au_name varchar2(100)
);
insert into author (au_id, au_name) values(1, 'ME');
create or replace function getAuthor(auName varchar2)
return author%rowtype
is
retval author%rowtype;
begin
select * into retval from author where au_name=auName;
return retval;
end;
declare
auth author%rowtype;
begin
auth := getAuthor('ME');
dbms_output.put_line(auth.au_id);
end;

Create table-returning function in package, using %ROWTYPE and TABLE OF

I have some tables which I cannot change. For each table, I need to create a FUNCTION inside a PACKAGE that returns a temporary table with the same data/layout as the original table. Instead of copying all table column definitions manually, I'd like to use statements such as %ROWTYPE.
I want to tell Oracle: "this function returns a table with the same layout as the original table XY".
Please have a look at this example. This is the (legacy) table:
CREATE TABLE TEST.Emp (
ID RAW(16),
NAME VARCHAR2(10)
);
/
These are the package and type definitions:
CREATE OR REPLACE TYPE TEST.row_Emp AS OBJECT (
ID RAW(16),
NAME VARCHAR2(10)
);
/
CREATE OR REPLACE PACKAGE TEST.Emp_PKG AS
TYPE t_Emp IS TABLE OF TEST.row_Emp INDEX BY BINARY_INTEGER;
FUNCTION F_Emp_Select (
VersionId INT DEFAULT NULL
) RETURN t_Emp;
END;
/
And here is the package body:
CREATE OR REPLACE PACKAGE BODY TEST.Emp_PKG AS
FUNCTION F_Emp_Select (
VersionId INT DEFAULT NULL
) RETURN t_Emp
AS
VersionVar INT := VersionId;
v_ret t_Emp;
BEGIN
SELECT
CAST(
MULTISET(
SELECT ID, NAME FROM TEST.Emp
) AS t_Emp) -- <== this is line 15
INTO v_ret
FROM dual;
RETURN v_ret;
END;
END;
/
If I execute this in SQLPlus, I get the following error:
Errors for PACKAGE BODY TEST.EMP_PKG:
LINE/COL ERROR
-------- -----------------------------------------------------------------
11/9 PL/SQL: SQL Statement ignored
15/22 PL/SQL: ORA-00902: invalid datatype
What am I doing wrong?
EDIT: I need this for a more complex case. This function will be used in other functions and procedures. For example, I need to be able to do a join on the result of the function:
SELECT ... FROM ...
INNER JOIN TEST.Emp_PKG.F_Emp_Select(...) ON ...
So I don't need the whole result.
Sorry for the confusion, I'm coming from SQL Server, where I have done such things many times.
If you need the result as something you can treat as a table, you could use a pipeline function:
CREATE OR REPLACE TYPE row_Emp AS OBJECT (
ID RAW(16),
NAME VARCHAR2(10)
);
/
CREATE OR REPLACE TYPE tab_Emp AS TABLE OF row_Emp
/
CREATE OR REPLACE PACKAGE Emp_PKG AS
FUNCTION F_Emp_Select (
VersionId INT DEFAULT NULL
) RETURN tab_Emp PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY Emp_PKG AS
FUNCTION F_Emp_Select (
VersionId INT DEFAULT NULL
) RETURN tab_EMP PIPELINED
AS
BEGIN
FOR row IN (SELECT ID, NAME FROM Emp) LOOP
PIPE ROW (row_Emp(row.ID, row.NAME));
END LOOP;
RETURN;
END;
END;
/
Notice the object type and the table type have to be declared at schema level; the table type can't be a PL/SQL-seclared table (collection) type as that wouldn't be usable in plain SQL, even within other PL/SQL. Unfortunately %ROWTYPE is a PL/SQL construct, so you can't use that to define your schema-level table type.
You can then do:
SELECT * FROM TABLE(Emp_PKG.F_Emp_Select(<optional versionId>));
... or use it in a join as:
SELECT ... FROM ...
INNER JOIN TABLE(TEST.Emp_PKG.F_Emp_Select(...)) ON ...
SQL FIddle demo.
Assuming that your goal is to return a collection, not a temporary table, it's a bit less complicated than your example
CREATE OR REPLACE PACKAGE emp_pkg
AS
TYPE emp_typ IS TABLE OF emp%rowtype index by binary_integer;
FUNCTION get_emps
RETURN emp_typ;
END;
CREATE OR REPLACE PACKAGE BODY emp_pkg
AS
FUNCTION get_emps
RETURN emp_typ
IS
l_emps emp_typ;
BEGIN
SELECT *
BULK COLLECT INTO l_emps
FROM emp;
RETURN l_emps;
END;
END;
Now, architecturally, I would be very concerned about a solution that involved selecting all the data from a table into a PL/SQL collection. PL/SQL collections have to be stored entirely in the session's SGA which is relatively expensive server RAM. If you have thousands or tens of thousands of rows in your table, that can be a pretty substantial amount of space on the server particularly if there may be many different sessions all calling these procedures at roughly the same time. If your tables all have a couple hundred rows and only one session at a time will be using these functions, maybe this approach will be sufficient.

How to return a table without knowing its structure in advance?

everyone I need to create a function in oracle that accepts a table name and return a collection the content of which is based the table that accepted.
I've been doing some search, many examples are of the form: first, define a table type; then, fill a table of that type and return it.
But I won't know the structure of the collection that need to be returned until the function is called, so I can not define a table type at the time of programming.
How can I make it? Thanks:-)
What I want to do is this:
Say, I have three tables--TABLE_A, TABLE_B, TABLE_C--each of which has different columns. Now I need to create a function func(table_name) that accept the table name(TABLE_A, TABLE_B, or TABLE_C) and return a collection the content of which is determined by the table name passed to the function. As the three tables have different columns, I can't create a type like "TColumnData " which you created. So, how should write the function?
One approach is to use global temporary tables.
create or replace
procedure test_procedure(table_name varchar2) as
begin
declare
table_or_view_not_exist exception;
pragma exception_init(table_or_view_not_exist, -942);
begin
begin
EXECUTE IMMEDIATE 'truncate TABLE TEMP_TABLE';
EXECUTE IMMEDIATE 'DROP TABLE TEMP_TABLE';
exception when table_or_view_not_exist then
DBMS_OUTPUT.PUT_LINE('Table TEMP_TABLE not found. skipping drop..');
end;
EXECUTE IMMEDIATE
'CREATE GLOBAL TEMPORARY TABLE TEMP_TABLE ON COMMIT PRESERVE ROWS AS
SELECT * FROM ' || table_name || ' ';
end;
end test_procedure;
then run the procedure:
execute test_procedure('TABLE_A');
once the procedure has completed execution, you can verify by:
select * from temp_table;

Resources