Oracle WITH AS SELECT with GRANT - oracle

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.

Related

ORACLE - Selecting Parameters for calling Procedure from Table

Is it possible to select the parameters for calling a procedure from the select statement?
EXECUTE PROCEDURE_NAME(para1,para2,para3,para4);
commit;
Is it possible to select para1,para2,para3,para4 from a select query?
EXECUTE PROCEDURE_NAME((SELECT PARA1,PARA2,PARA3,PARA4 FROM TABLEA))
COMMIT;
I do not have access to modify the procedure.
As a slight variation on what #vc74 suggested, you could just replace your EXECUTE command (which, assuming this is SQL*Plus or SQL Developer anyway, is just a wrapper for an anonymous block anyway) with an explicit anonymous block:
begin
for r in (SELECT PARA1,PARA2,PARA3,PARA4 FROM TABLEA) loop
PROCEDURE_NAME(r.PARA1,r.PARA2,r.PARA3,r.PARA4);
end loop;
end;
/
(I've left the bits from your original call uppercase and the new bits lower case mostly to distinguish them.)
Using a loop just means you don't need to declare local variables and select into those. It would also allow you to process multiple rows from the table, though I see form a comment you only expect one row. However, the flip side of that is it won't complain if there are no rows, or if there is more than one row, as the variable approach would do.
You could also use a record type to avoid declaring all the parameters separately:
declare
l_row tablea%rowtype;
begin
SELECT * into l_row FROM TABLEA;
PROCEDURE_NAME(l_row.PARA1,l_row.PARA2,l_row.PARA3,l_row.PARA4);
end;
/
This now does expect exactly one row to be found in the table.
You can call the functions in sql. So if you are able to create a function in your schema then you can do the following:
create a function function_name in your schema that calls the procedure procedure_name and returns some dummy result
use this function in sql query: select function_name(para1,para2,para3,para4) from tablea
example of function:
create or replace function function_name(
p1 varchar2,
p2 varchra2,
p3 varchar2,
p4 varchar2
) return number
is
begin
procedure_name(p1,p2,p3,p4); -- here you execute the procedure
return null;
end;

Why EXECUTE IMMEDIATE is needed here?

I am a SQL Server user and I have a small project to do using Oracle, so I’m trying to understand some of the particularities of Oracle and I reckon that I need some help to better understand the following situation:
I want to test if a temporary table exists before creating it so I had this code here:
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
SELECT COUNT(*) INTO table_count FROM all_tables WHERE table_name = 'TEST';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
END IF;
END;
It works normally, so after I executed it once, I added an else statement on my IF:
ELSE
insert into test (hello) values ('hi');
Executed it again and a line was added to my test table.
Ok, my code was ready and working, so I dropped the temp table and tried to run the entire statement again, however when I do that I get the following error:
ORA-06550: line 11, column 19:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 11, column 7:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Then I changed my else statement to this and now it works again:
ELSE
EXECUTE IMMEDIATE 'insert into test (hello) values (''hi'')';
My question is why running individually I can simply use the insert instead of the EXECUTE IMMEDIATE and also why my SELECT statement right after BEGIN still works when all the rest appears to need EXECUTE IMMEDIATE to run properly?
The whole PL/SQL block is parsed at compile time, but the text within a dynamic statement isn't evaluated until runtime. (They're close to the same thing for an anonymous block, but still distinct steps).
Your if/else isn't evaluated until runtime either. The compiler doesn't know that the table will always exist by the time you do your insert, it can only check whether or not it exists at the point it parses the whole block.
If the table does already exist then it's OK; the compiler can see it, the block executes, your select gets 1, and you go into the else to do the insert. But if it does not exist then the parsing of the insert correctly fails with ORA-00942 at compile time and nothing in the block is executed.
Since the table creation is dynamic, all references to the table have to be dynamic too - your insert as you've seen, but also if you then query it. Basically it makes your code much harder to read and can hide syntax errors - since the dynamic code isn't parsed until run-time, and it's possible you could have a mistake in a dynamic statement in a branch that isn't hit for a long time.
Global temporary tables should not be created on-the-fly anyway. They are permanent objects with temporary data, specific to each session, and should not be created/dropped as part of your application code. (No schema changes should be made by your application generally; they should be confined to upgrade/maintenance changes and be controlled, to avoid errors, data loss and unexpected side effects; GTTs are no different).
Unlike temporary tables in some other relational databases, when you create a temporary table in an Oracle database, you create a static table definition. The temporary table is a persistent object described in the data dictionary, but appears empty until your session inserts data into the table. You create a temporary table for the database itself, not for every PL/SQL stored procedure.
Create the GTT once and make all your PL/SQL code static. If you want something closer to SQL Server's local temporary tables then look into PL/SQL collections.
PL/SQL: ORA-00942: table or view does not exist
It is compile time error, i.e. when the static SQL is parsed before even the GTT is created.
Let's see the difference between compile time and run time error:
Static SQL:
SQL> DECLARE
2 v number;
3 BEGIN
4 select empno into v from a;
5 end;
6 /
select empno into v from a;
*
ERROR at line 4:
ORA-06550: line 4, column 26:
PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 4, column 1:
PL/SQL: SQL Statement ignored
Dynamic SQL:
SQL> DECLARE
2 v number;
3 BEGIN
4 execute immediate 'select empno from a' into v;
5 end;
6 /
DECLARE
*
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at line 4
In the 1st PL/SQL block, there was a semantic check at compile time, and you could see the PL/SQL: ORA-00942: table or view does not exist. In the 2nd PL/SQL block, you do not see the PL/SQL error.
Bottomline,
At compile time it is not known if the table exists, as it is
only created at run time.
In your case, to avoid this behaviour, you need to make the INSERT also dynamic and use EXECUTE IMMEDIATE. In that way, you can escape the compile time error and get the table created dynamically and also do an insert into it dynamically at run time.
Having said that, the basic problem is that you are trying to create GTT on the fly which is not a good idea. You should create it once, and use it the way you want.
I have modified your code a litle bit and it works as far as logic is concerned. But as exp[lained in earlier posts creating GTT on the fly at run time is not at all is a goood idea.
--- Firstly by dropping the table i.e NO TABLE EXISTS in the DB in AVROY
SET serveroutput ON;
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST'; --Added the line just to drop the table as per your comments
SELECT COUNT(*)
INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
dbms_output.put_line('table created');
ELSE
INSERT INTO AVROY.test
(hello
) VALUES
('hi'
);
END IF;
END;
--------------------OUTPUT-----------------------------------------------
anonymous block completed
table created
SELECT COUNT(*)
-- INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
COUNT(*)
------
1
--------
-- Second option is without DROPPING TABLE
SET serveroutput ON;
DECLARE
table_count INTEGER;
var_sql VARCHAR2(1000) := 'create GLOBAL TEMPORARY table TEST (
hello varchar(1000) NOT NULL)';
BEGIN
--EXECUTE IMMEDIATE 'DROP TABLE AVROY.TEST';
SELECT COUNT(*)
INTO table_count
FROM all_tables
WHERE table_name = 'TEST'
AND OWNER = 'AVROY';
IF table_count = 0 THEN
EXECUTE IMMEDIATE var_sql;
dbms_output.put_line('table created');
ELSE
INSERT INTO AVROY.test
(hello
) VALUES
('hi'
);
dbms_output.put_line(SQL%ROWCOUNT||' Rows inserted into the table');
END IF;
END;
-------------------------------OUTPUT-------------------------------------
anonymous block completed
1 Rows inserted into the table
---------------------------------------------------------------------------

Table variable in Oracle stored procedure

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.

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;

Oracle Execute Immediate with DDL and Nested table

I have a problem trying to use an Execute Immediate statement containing a CREATE TABLE statement and a user defined Table Type. I get error ORA-22905 on Oracle 11g.
Is there any workaround to solve this issue?
CREATE TYPE MY_TABLE_TYPE AS TABLE OF VARCHAR2(30);
/
DECLARE
MT MY_TABLE_TYPE;
BEGIN
SELECT * BULK COLLECT INTO MT FROM DUAL;
-- Two steps
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLE1 (A VARCHAR2(30))';
EXECUTE IMMEDIATE 'INSERT INTO MY_TABLE1 SELECT * FROM TABLE(:T)' USING MT; -- OK
-- One step
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLE2 AS SELECT * FROM TABLE(:T)' USING MT; -- ERROR ORA-22905
END;
The real code for the SELECT * FROM TABLE(:T) is dynamic (main table name is temporary) and slow. That's why I try to avoid creating the table in two steps (as done with MY_TABLE1). Also with two steps I can't use SELECT * but I have to specify all the columns (variable amount and over 100 columns).
There is likely a way to completely avoid this issue. Skip the bulk collect and use a simple CREATE TABLE MY_TABLE AS SELECT * FROM DUAL; That may be an over-simplification of the real logic to gather the data. But there is almost always a way to bypass a bulk collect and store the data directly in an object with just SQL.
If a PL/SQL solution is truly needed, the error ORA-22905: cannot access rows from a non-nested table item can be avoided by creating an object type and creating the table based on that type. This may not solve the performance issue, but at least this avoids the need to re-specify all the columns in the table DDL.
CREATE TYPE MY_TABLE_OBJECT IS OBJECT
(
A VARCHAR2(30)
);
CREATE TYPE MY_TABLE_TYPE2 AS TABLE OF VARCHAR2(30);
DECLARE
MT MY_TABLE_TYPE2;
BEGIN
SELECT * BULK COLLECT INTO MT FROM DUAL;
EXECUTE IMMEDIATE 'CREATE TABLE MY_TABLE2 OF MY_TABLE_OBJECT';
EXECUTE IMMEDIATE 'INSERT INTO MY_TABLE2 SELECT * FROM TABLE(:T)' USING MT;
END;
/

Resources