output data, dynamic sql - sql

I have some problem with dynamic SQL.
I created table, after that inserted some data - it works fine.
But i have no idea how to display data. My code:
declare
begin
execute immediate 'create table name(tabl_name varchar2(30),id number)';
execute immediate 'insert into name(tabl_name,id) (select ''something'',id from table3)';
commit;
and now i would like to display name table content. How to do that? Should i use cursor with dynamic sql? Thanks in advance.

You can use cursor to loop through the records:
declare
v_tabl_name varchar2(30);
v_id number;
res_cur SYS_REFCURSOR;
begin
execute immediate 'create table name(tabl_name varchar2(30),id number)';
execute immediate 'insert into name(tabl_name,id) (select ''something'',id from table3)';
open res_cur for 'select tabl_name, id from name';
LOOP
FETCH res_cur INTO v_tabl_name, v_id;
EXIT WHEN res_cur%NOTFOUND;
dbms_output.put_line(v_tabl_name);
dbms_output.put_line(v_id);
END LOOP;
close res_cur;
end;

Related

How can I set a cursor on a select statement with dynamic table_name in PL/SQL?

I created a PL/SQL procedure that takes the name of user and shows a list of his tables' content. The problem is that I couldn't set the cursor to accept a different table name each time and show its content.
Here is my code which is not complete yet.
set serveroutput on;
create or replace procedure userTable(name dba_tables.owner%type)
is
cursor cur is select table_name from dba_tables where owner = upper(name);
cursor cur2(tab_name dba_tables.table_name%type) is select * from name.tab_name;
V$FILEP UTL_FILE.FILE_TYPE;
begin
if existUser(name) = true then
V$FILEP := UTL_FILE.FOPEN('DIRTEST','TEST15.UTL','W');
for x in cur loop
UTL_FILE.PUT_LINE(V$FILEP,to_char(x.table_name));
end loop;
UTL_FILE.FCLOSE(V$FILEP);
else
dbms_output.put_line('user invalid');
end if;
end;
/

How to execute results of dbms_output.put_line

There is a table contains this kind of data: select to_char(sysdate,'day') from dual in a column. I want to get results of the every query that the table keeps.
My result set should be the result of select to_char(sysdate,'day') from dual query. So in this case it is a tuesday.
SO_SQL_BODY is Varchar2.
I wrote this code but it returns only table data.
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
END LOOP;
END a_proc;
DECLARE
res varchar2(4000);
sql_str varchar2(1000);
BEGIN
FOR r IN
(select SO_SQL_BODY FROM SO_SUB_VARS WHERE SO_SQL_BODY IS NOT NULL
)
LOOP
sql_str := r.SO_SQL_BODY;
EXECUTE immediate sql_str INTO res;
dbms_output.put_line(sql_str);
dbms_output.put_line('***********************');
dbms_output.put_line(res);
dbms_output.put_line('***********************');
END LOOP;
END;
/
Try this - iterate to not null records - execute them and print the result.This script works supposing the fact that SO_SQL_BODY contains a query which projects only one column.Also if the projection is with more than two columns then try to use a refcursor and dbms_sql package
İf var_names(indx).SO_SQL_BODY output is a runnable sql text;
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
EXECUTE IMMEDIATE var_names(indx).SO_SQL_BODY;
END LOOP;
END a_proc;
You don't need a full cursor for this example. An implicit one would make it a lot shorter.
create or replace procedure a_proc is
lReturnValue varchar2(250);
begin
for q in (select so_sql_body from so_sub_vars group by so_sql_body)
loop
execute immediate q.so_sql_body into lReturnValue;
dbms_output.put_line(lReturnValue);
end loop;
end a_proc;
You should add an exception handler that will care for cases where there is a bad SQL query in your table. Also note that executing querys saved in a database table is your entry point to SQL injection.

Dynamic SQL LOOP

Dynamic SQL is not my friend, basically the idea is that I can use the procedure with the "p_in_table" paramter to get the number of rows contained in the table.
CREATE OR REPLACE PROCEDURE how_many_rows(p_in_table VARCHAR2)
IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
BEGIN
v_cur_txt := 'SELECT * FROM ' || p_in_table;
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
dbms_output.put_line(v_rowcount);
END;
Would preciate it if someone would tell me what am I doing wrong?
The problem is that you not iterating through cursor - no fetch statement or something like that, so, basically, you have an infinite loop. To avoid this you need to do something like this:
CREATE OR REPLACE PROCEDURE how_many_rows
(p_in_table VARCHAR2) IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
v_row SOME_TABLE%ROWTYPE; --add row variable
BEGIN
v_cur_txt := 'SELECT * FROM '|| p_in_table;
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
FETCH v_cur_cur INTO v_row; --fetch a row in it
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
DBMS_OUTPUT.PUT_LINE(v_rowcount);
END;
But, as you can see, to do this you need to know, what table you're quering, so this is not general solution. Maybe there is a workaround for this, but i suggest, you use more simple and efficient approach, for example with EXECUTE IMMEDIATE:
CREATE OR REPLACE PROCEDURE HOW_MANY_ROWS(p_in_table VARCHAR2)
IS
v_tmp NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM ' || p_in_table INTO v_tmp;
DBMS_OUTPUT.PUT_LINE(v_tmp);
END;
Ok, I gave a thought on how to achieve this using your way, and here is what i've ended up with - just fetch ROWNUM from your table, every table has it and you know it's type - NUMBER. So this procedure will work in general case:
CREATE OR REPLACE PROCEDURE how_many_rows
(p_in_table VARCHAR2) IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
v_row NUMBER; --add rownum variable
BEGIN
v_cur_txt := 'SELECT ROWNUM FROM '|| p_in_table; --select only rownum from target table
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
FETCH v_cur_cur INTO v_row; --fetch rownum in it
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
DBMS_OUTPUT.PUT_LINE(v_rowcount);
END;

returning into clause with dynamic sql

I have copied and pasted this from http://www.oracle-base.com/articles/misc/dml-returning-into-clause.php
But this code goes on and on while executing ...
SET SERVEROUTPUT ON
DECLARE
TYPE t_tab IS TABLE OF t1.id%TYPE;
l_tab t_tab;
BEGIN
EXECUTE IMMEDIATE 'UPDATE t1
SET description =' ||'description '||'
RETURNING id INTO :l_tab'
RETURNING BULK COLLECT INTO l_tab;
FOR i IN l_tab.first .. l_tab.last LOOP
DBMS_OUTPUT.put_line('UPDATE ID=' || l_tab(i));
END LOOP;
COMMIT;
END;
Any mistypes or errors in the code ?
You have to add a / at the end of the PL/SQL block to execute it.

Dynamic SQL within cursor

My dynamic sql below to alter a table & create columns based on the output of a query is giving error.
Query :
DECLARE
CURSOR c1 is select distinct WP_NO from temp;
cnum VARCHAR2(255);
BEGIN
FOR cnum in c1
LOOP
EXECUTE IMMEDIATE 'Alter table temp_col add (:1 varchar2(255))' using cnum;
END LOOP;
COMMIT;
END;
Error :
PLS-00457: expressions have to be of SQL types
This is happening because bind variables are not allowed in DDL statements.
Consider trying it without using the bind variable:
DECLARE
CURSOR c1 is select distinct WP_NO from temp;
cnum VARCHAR2(255);
BEGIN
FOR cnum in c1
LOOP
EXECUTE IMMEDIATE 'Alter table temp_col add ('|| cnum ||' varchar2(255))';
END LOOP;
COMMIT;
END;
You have a conflict with the cnum symbol, which you use both as a local variable and for the current row of the cursor.
You probably want this:
DECLARE
CURSOR c1 is select distinct WP_NO from temp;
BEGIN
FOR current_row in c1
LOOP
EXECUTE IMMEDIATE 'Alter table temp_col add (:1 varchar2(255))' using current_row.WP_NO;
END LOOP;
COMMIT;
END;
As you can see, you don't need to declare the current_row variable that you use in the for loop.