Form dynamic query and execute in oracle - oracle

I want to form a dynamic query for drop and create view and execute it.
for C in Cursor
LOOP
ViewName :='View_'|| ID;
DropViewSQL := DropViewSQL || 'DROP VIEW '||ViewName ||';' ;
CreateViewSQL := CreateViewSQL || 'CREATE VIEW '|| ViewName ||' AS SELECT * from xyz;';
END LOOP;
//Some Insert and update statements to be executed before drop and create view basically i want to create a new view after these Insert and update statements
DBMS_OUTPUT.PUT_LINE(DropViewSQL);
DBMS_OUTPUT.PUT_LINE(CreateViewSQL);
EXECUTE IMMEDIATE DropViewSQL;
EXECUTE IMMEDIATE CreateViewSQL;
This gives error - PLS-00382: expression is of wrong type
I want to execute this dynamically formed query.
Solution:
I have used array instead of cursor to store the resultset. Performed the insert update and then loop on array.
Thanks for the suggestions..

You can't have multiple statements in one execute immediate call; you'd need to call that inside the loop:
for C in Cursor
LOOP
ViewName := 'View_'|| C.ID;
DropViewSQL := 'DROP VIEW ' || ViewName ;
CreateViewSQL := 'CREATE VIEW ' || ViewName || ' AS SELECT * from xyz';
DBMS_OUTPUT.PUT_LINE(DropViewSQL);
DBMS_OUTPUT.PUT_LINE(CreateViewSQL);
EXECUTE IMMEDIATE DropViewSQL;
EXECUTE IMMEDIATE CreateViewSQL;
END LOOP;
Notice that the dynamic SQL statement does not have a semicolon on the end. You also might not need to do a separate drop step; create or replace view might be more appropriate, since that retains grants.
Without any further information about the 'Some statements to be executed before drop and create view' part it isn't clear where those would fit in.
But that doesn't explain the PLS-00382. You haven't shown what Cursor is, and I suspect it doesn't like that. Since cursor is a reserved word yours (hopefully) won't be called that, but don't know if it's an explicit cursor declared earlier or an implicit cursor with the query in-place here. Either way you need to show what that is and what it is doing. If that is OK then maybe one of the other statements you're removed is causing the error. Without all the relevant code and the line number of the error, it's hard to guess.
If you really have to generate the commands together, then do something else before executing them, you could store them in a PL/SQL table:
DECLARE
cursor cur is select view_name as id from user_views;
/* or whatever your real cursor is */
type sqltab is table of varchar2(200);
dropsqltab sqltab;
createsqltab sqltab;
viewname varchar2(30);
BEGIN
dropsqltab := sqltab();
createsqltab := sqltab();
for C in cur
LOOP
ViewName := 'View_'|| C.ID;
dropsqltab.extend();
dropsqltab(dropsqltab.count) := 'DROP VIEW ' || ViewName ;
createsqltab.extend();
createsqltab(createsqltab.count) := 'CREATE VIEW ' || ViewName
|| ' AS SELECT * from xyz';
END LOOP;
/* other commands */
FOR i IN 1 .. dropsqltab.count LOOP
DBMS_OUTPUT.PUT_LINE(dropsqltab(i));
DBMS_OUTPUT.PUT_LINE(createsqltab(i));
EXECUTE IMMEDIATE dropsqltab(i);
EXECUTE IMMEDIATE createsqltab(i);
END LOOP;
END;
/
You still haven't said what relationship, if any, there is between the drop/create statements and the insert/update statements. If they are related you can extract values from the PL/SQL table more than once. If they aren't, well, I don't understand the restriction on the order things are done.

Related

Trying to run consecutive queries in Oracle

I am creating a temp table
CREATE TABLE TMP_PRTimeSum AS
SELECT DISTINCT p.employee,
SUM(p.wage_amount) AS wage_sum,
SUM(p.hours) AS hour_sum
I then want to do a select from that temp table and show the results. I want to do it all in one query. It works if I run them as two separate queries. Is there any way to do this in one query?
CREATE GLOBAL TEMPORARY TABLE a
ON COMMIT PRESERVE ROWS
AS
select * from b;
(add where 1=0 too if you didn't want to initially populate it for the current session with all the data from b).
Is there any way to do this in one query?
No, you need to use one DDL statement (CREATE TABLE) to create the table and one DML statement (SELECT) to query the newly created table.
If you want to be really pedantic then you could do it in a single PL/SQL statement:
DECLARE
cur SYS_REFCURSOR;
v_employee VARCHAR2(20);
v_wage NUMBER;
v_hours NUMBER;
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE TMP_PRTimeSum (employee, wage_sum, hour_sum) AS
SELECT P.EMPLOYEE,
SUM(P.WAGE_AMOUNT),
SUM(P.HOURS)
FROM table_name p
GROUP BY p.Employee';
OPEN cur FOR 'SELECT * FROM TMP_PRTimeSum';
LOOP
FETCH cur INTO v_employee, v_wage, v_hours;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_employee || ', ' || v_wage || ', ' || v_hours);
END LOOP;
END;
/
db<>fiddle here
However, you are then wrapping the two SQL statements in a PL/SQL anonymous block so, depending on how you want to count it then it is either one composite statement, two SQL statements or three (2 SQL and 1 PL/SQL) statements.

How to create a cursor within for loop of PL/SQL Code and bulk collect results into table

I have a database with many users that have the same table (by same I mean same columns but different data). I would like to run the same query on these tables and bulk collect the results into a temporary table or any other way of viewing.
So far I have the code shown below:
DECLARE
TYPE PDTABLE_12SEGTBL IS TABLE OF MTO_SG2420.PDTABLE_12%ROWTYPE;
COLLECTIONTBL PDTABLE_12SEGTBL;
LoopIteration pls_integer;
CompTblName varchar2(61);
CURSOR MTO_Cursor IS
SELECT owner, table_name
FROM ALL_TABLES
WHERE OWNER LIKE 'MTO_K%'
AND TABLE_NAME = 'PDTABLE_12';
BEGIN
LoopIteration :=1;
FOR item IN MTO_Cursor
LOOP
CompTblName := item.owner || '.pdtable_12';
DBMS_OUTPUT.PUT_LINE('Loop Iteration ' || LoopIteration || ' CompTblName' || CompTblName);
LoopIteration := LoopIteration + 1;
END LOOP;
END;
The tables that I would like to run the query on look like this:
MTO_K01.pdtable_12
MTO_K02.pdtable_12
MTO_K03.pdtable_12
MTO_K04.pdtable_12
MTO_K05.pdtable_12
In the CompTblName variable, I store the complete table name including username successfully through each iteration.
My question is how can I add a query to the code above that runs a select statement on the variable CompTblName and pushes the results into the table that I created (COLLECTIONTBL). I searched in this forum and other places and saw that I can do this using fetch command. However, fetch command needs to be placed within a cursor which gives an error every time I place it in a loop. It is important to note that I would like to concatenate the results from all iterations into COLLECTIONTBL.
You need to use execute immediate statement, which allows to create and run dynamic SQL:
FOR item IN MTO_Cursor LOOP
CompTblName := item.owner || '.pdtable_12';
execute immediate 'insert into COLLECTIONTBL select * from ' || CompTblName;
END LOOP;

ORACLE: Cursor with dynamic query - throws error "invalid identifier" for cursor field

I have a logic to implement where I have to use dynamic sql(column names and where clause is decided on the fly).So here my cursor(emp_ref_cursor) has a dynamic sql, and has 3 cursor fields(emp_id,emp_name,dept).
Using these cursor fields in WHERE clause I am trying to execute another dynamic sql inside the loop.Bt oracle isn't able to identify the cursor field and throws an error like "ORA-00904: "EMP_REC"."EMP_ID": invalid identifier" though I am able to output emp_rec.emp_id through DBMS_OUTPUT.
NOTE: Please don't comment on the code quality this is not the actual code.
This is just used to describe the problem. I can't post the actual code due to
some compliance related stuff.
DECLARE
emp_ref_cursor sys_refcursor;
v_sql varchar2(3900);
TYPE emp_rec_type IS RECORD (emp_id number,emp_name varchar2(100),dept_id varchar2(100));
emp_rec emp_rec_type;
v_dept_id number:='1234';
v_dob varchar2(100);
v_desig varchar2(100);
x_dynamic_col_1 varchar2(100):='dob'; --dynamic column(based on some condition)
x_dynamic_col_2 varchar2(100):='designation'; --dynamic column(based on some condition)
x_dynamic_col_3 varchar2(100):='emp_id'; --dynamic column(based on some condition)
BEGIN
v_sql:='SELECT emp_id,emp_name,dept FROM employee WHERE dept_id=' || v_dept_id;
OPEN emp_ref_cursor FOR v_sql;
LOOP
FETCH emp_ref_cursor INTO emp_rec;
exit WHEN emp_ref_cursor%NOTFOUND;
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
DBMS_OUTPUT.PUT_LINE(stmt);
--Prints the SQL query as expected
DBMS_OUTPUT.PUT_LINE('emp_rec.emp_id:'||emp_rec.emp_id);
--Displays the value!!!
execute immediate stmt into v_dob, v_desig;
--But why is it saying emp_rec.emp_id is invalid identifier??
END LOOP;
END;
You have emp_rec defined as a local PL/SQL variable. None of the PL/SQL data is in scope to the dynamic SQL execution. When it is executed it as if you tried to run the statement - as it is displayed by your dbms_output standalone in a separate SQL context. If you did that it would be clear that emp_rec doesn't exist to the query.
You refer to it you would need to use a bind variable:
WHERE emp_id=:dynamic_col_3';
And then execute it with:
execute immediate stmt using emp_rec.emp_id;
But you can't use the x_dynamic_col_3 local variable in the using clause. Since - in this example anyway - the query would also need to change to use a different table column is the dynamic record field changed - that doesn't seem too much of a problem. But you said the where clause will change on the fly too. In that case you could have another local variable that you set to the relevant x field before the executin.
You have incorrect using of EXECUTE IMMEDIATE. You don't need to put INTO clause to SQL query. Use this instead:
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
execute immediate stmt into v_dob, v_desig;

Oracle pl sql dynamic using clause

I have a question about "dynamic using clause" in execute immediate statement. I need to set dynamically the "execute immediate statement" and the using clause as well. I don't know the table structure, but I know only the name of the table, and I need to do an operation update on it.
So I wrote a function (through user_tab_columns and user user_constraints tables) to set a variable with the update statement and the bind_variable but now I need to set the using clause with the list of variable.
Example:
CREATE TABLE table1
(
rec1 VARCHAR2(10 BYTE) NULL,
rec2 DATE NULL,
rec3 number(9) not null
);
declare
TYPE cur_type IS REF CURSOR;
cur cur_type;
table_list table1%ROWTYPE;
sqlstring varchar2(400);
begin
OPEN cur FOR sqlstring;
LOOP
FETCH cur INTO table_list;
EXIT WHEN cur%NOTFOUND;
sqlstring:=function1('table1');
-- that returns sqlstring:='update table1 set rec1=:1 , rec2=:2 , rec3=:3 where rec_id=:c4';
execute immediate sqlstring using table_list.rec1, table_list.rec2, table_list.rec3, table_list.rec_id;
END LOOP;
close cur;
end;
I need to implement dynamically the list of variables of the cursor table_list.
"execute immediate sqlstring using table_list.rec1, table_list.rec2, table_list.rec3, table_list.rec_id"
Does anybody know how to solve this problem?
Thanks a lot for your replies.
The problem is that I'm assuming I don't know the table's structure and so the list of variables of the cursor table_list table1%ROWTYPE.
So I can't explicit table_list.rec1, table_list.rec2 ... in the using clause.
If I use only table_list as variable
begin
OPEN cur FOR sqlstring;
LOOP
FETCH cur INTO table_list;
EXIT WHEN cur%NOTFOUND;
sqlstring:=function1('table1');
execute immediate sqlstring using table_list;
END LOOP;
close cur;
I got the error:" 00457 Expressions have to be of SQL types"
http://psoug.org/oraerror/PLS-00457.htm
Error Cause:
An expression of wrong type is in USING or dynamic RETURNING clause. In USING or dynamic RETURNING clause, an expression cannot be of non-SQL types such as BOOLEAN, INDEX TABLE, and record.
I need a way to retrive not only the values but also the list of variables of the cursor table_list first.
But maybe it's impossible and I have to find a work around.
If I will find something interesting I will post.
Thankyou.
Try to replace your execute immediate to full use of dbms_sql.
http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996891
And usefull for you will be bind_array function from this package.
Use dynamic PL/SQL, unless you can re-factor the original statement and just plug the values into it.
declare
v_string constant varchar2(32767) := 'update test1 set a = :1, b = :2';
v_using_string varchar2(32767);
begin
--Create dynamic using string.
--For example, let's say you want to pass in the values "1" for each NUMBER column.
select listagg(1, ',') within group (order by null)
into v_using_string
from user_tab_columns
where table_name = 'TEST1'
and data_type = 'NUMBER';
--Execute the original dynamic SQL, adding the USING string.
execute immediate '
begin
execute immediate '''||v_string||''' using '||v_using_string||';
end;
';
end;
/
You can either use DBMS_SQL package:
open a cursor using dbms_sql.open_cursor
parse the statement using dbms_sql.parse
bind variables in a loop using dbms_sql.bind_variable
execute the statement using dbms_sql.execute
and finally close the cursor using dbms_sql.close_cursor
Or EXECUTE IMMEDIATE of anonymous PL/SQL block, which performs a dynamically created EXECUTE IMMEDIATE (this approach is not suitable for returning data). See Answer of #JonHeller.

pl/sql: select as argument/parameter of function in oracle

I've got problem with creating function. I'll try explain what the problem is:
The task is about creating function which is based on select first_name from employees where dept_id=10;. New function MUST have v_select_statement (which I wrote) as argument(sth like this: v_select_statement := select first_name from employees where dept_id=10;. Selet like you can see generating list of names. These names should be in new table which is created in this function too. New table should have name e.g new_table.
Problem is that I don't know how to do it. I tried something like this:
create or replace
FUNCTION create_new_tab (v_select_statement VARCHAR2) RETURN NUMBER
is
b first_name.employees%TYPE;
begin
--here i don`t know how to assign select statement to cursor or execute immediate. Any ideas? i tried create sth like:
-- execute immediate 'v_select_statement into b ';
execute immediate 'CREATE TABLE new_tab (i VARCHAR2(50))';
execute immediate 'insert into new_tab values (statement_result)';
--don`t know how and what to put as statement_result. I know that i could use cursor, but ---how in this case?
return 1;
exception
when others then
dbms_output.put_line(SQLERRM);
return 0;
END create_new_tab
;
Can you help me, because I give up. I have no idea how to solve this problem.
Please help me if you can.
What you can do is the following:
create or replace FUNCTION create_new_tab (v_select_statement VARCHAR2) RETURN NUMBER
is
b employees.first_name%TYPE;
cur SYS_REFCURSOR;
begin
OPEN cur for v_select_statement;
execute immediate 'CREATE TABLE new_tab (i VARCHAR2(50))';
LOOP
FETCH cur into b;
EXIT WHEN cur%NOTFOUND;
execute immediate 'insert into new_tab values (:statement_result)' USING b;
END LOOP;
CLOSE cur;
return 1;
exception
when others then
dbms_output.put_line(SQLERRM);
return 0;
END create_new_tab
;
Couldn't test it without the tables but it may point you in the right direction.
But as Avrajit already said, I would suggest creating the new_tab beforehand and then insert into that table normalle without Dynamic-SQL.
You also have to keep in mind, that this function can only be run once, as it will result in an error if you try to create the table when it already exists.
This is a Function created for your referenec.
What i suggest you to create a table beforehand and then
try to insert data into it. Please try this code and let me
know if it works for you. Thanks
Create or replace function test_func return number
as
cursor cr is select name from avrajit
where department='.NET';
name_av avrajit.name%type;
begin
for rec in cr
loop
insert into av_test(dpt) values(rec.name);
return 1;
end loop;
exception
when others then
return 0;
end test_func;
I dont know what is purpose of your function. But if you still want to do try this
create or replace
FUNCTION create_new_tab (v_select_statement VARCHAR2) RETURN NUMBER
is
b first_name.employees%TYPE;
begin
--here i don`t know how to assign select statement to cursor or execute immediate. Any ideas? i tried create sth like:
-- execute immediate 'v_select_statement into b ';
execute immediate 'CREATE TABLE new_tab (i VARCHAR2(50))';
execute immediate 'insert into new_tab values ( ' || v_select_statement || ' )'; --don`t know how and what to put as statement_result. I know that i could use cursor, but ---how in this case?
return 1;
exception
when others then
dbms_output.put_line(SQLERRM);
return 0;
END create_new_tab
;`

Resources