How can I print a multi line result using PL/SQL? - sql

PROCEDURE A(
...
BEGIN
stmt := 'select * from '||src;
execute immediate stmt;
dbms_output.put_line(??);
END A;

If you know the structure of the table named in "src" when writing the code then you can do this:
PROCEDURE A IS
...
l_cur sys_refcursor;
BEGIN
stmt := 'select * from '||src;
open l_cur for stmt;
loop
fetch l_cur into ??; -- record or list of variables that matches columns of "src"
exit when l_cur%notfound;
dbms_output.put_line(??);
end loop;
close l_cur;
END A;
If you will not know the structure until run time then you will need to use the DBMS_SQL package, which is very powerful but not simple.

I'm not sure wether this is working with your "execute immediate stmt" approach, but with static Sql, following is working for me:
for my_result in
(
select * from my_table tbl
where ...
order by tbl.my_id_col
) loop
dbms_output.put_line(my_result.field1 || ', ' || my_result.field2 || ...);
end loop;

Related

How to execute from select result (oracle sql)

I created table with grants list. How I can execute grants from this table ?
Something like
select * from grants_table;
then EXECUTE IMMEDIATE result from select
You could write a loop
begin
for grant in (select * from grants_table)
loop
execute immediate grants.column_with_ddl;
end loop;
end;
Most likely, you'll want to do some amount of logging/ exception handling/ etc.
If your table is of considerable size you could take advantage of the bulk operations and binds, something among the lines:
DECLARE
TYPE cursor_ref IS REF CURSOR;
c1 cursor_ref;
TYPE grants_t IS TABLE OF grants%ROWTYPE;
grants_tab grants_t;
rows_fetched NUMBER;
errors NUMBER;
dml_errors EXCEPTION;
PRAGMA exception_init(dml_errors, -24345);
BEGIN
OPEN c1 FOR 'SELECT * FROM grants';
FETCH c1 BULK COLLECT INTO grants_tab;
rows_fetched := c1%ROWCOUNT;
DBMS_OUTPUT.PUT_LINE('Number of grants: ' || TO_CHAR(rows_fetched));
BEGIN
FORALL i IN 1 .. grants_tab.count
EXECUTE IMMEDIATE '< some ddl> :1' USING grants_tab(i);
EXCEPTION WHEN dml_errors THEN
errors := SQL%BULK_EXCEPTIONS.COUNT;
DBMS_OUTPUT.PUT_LINE('Number of errors is ' || errors);
FOR j IN 1..errors LOOP
DBMS_OUTPUT.PUT_LINE('Error ' || j || ' occurred on iteration ' || SQL%BULK_EXCEPTIONS(j).ERROR_INDEX);
DBMS_OUTPUT.PUT_LINE('Oracle error is ' || SQLERRM(-SQL%BULK_EXCEPTIONS(j).ERROR_CODE));
END LOOP;
END;
END;
/

How to Create Views for all Tables in my DB

I have written the code below to correct all table in my db.
Please review and correct it.
Declare
Name Varchar2(100);
A Number:=0;
Cursor C1 Is
Select Table_Name From Tabs;
Begin
Open C1;
Loop
A:=A+1;
Fetch C1 Into Name;
Exit When C1%Notfound;
Execute Immediate 'Create Or Replace View Tab||A
As
Select * From Name';
End Loop;
Close C1;
End
Please correct my Code
This forum is not a place to give people assigments, please ask questions about the issue you are running into and we are willing to help you. Please try doing a better job next time.
Your execute immediate string should be:
Execute Immediate
'Create Or Replace View Tab' ||A|| '
As
Select * From ' || Name;
and don't forget the ; after the final end
fixed your code:
Declare
Name Varchar2(100);
A Number:=0;
Cursor C1 Is
Select Table_Name From Tabs;
Begin
Open C1;
Loop
A:=A+1;
Fetch C1 Into Name;
Exit When C1%Notfound;
Execute Immediate 'Create Or Replace View Tab'||A||'
As
Select * From "'||Name||'"';
End Loop;
Close C1;
End;
/
But I would do something like this:
declare
procedure p_exec(pCMD in varchar2, pPrintOnly varchar2 default 'n') is
begin
if pPrintOnly='y' then
dbms_output.put_line(pCMD);
else
execute immediate pCMD;
dbms_output.put_line(pCMD);
end if;
end;
begin
for r in (Select rownum rn, Table_Name From Tabs) loop
p_exec(
utl_lms.format_message(
'Create Or Replace View Tab%s As Select * From "%s"'
, to_char(r.rn,'fm9999')
, r.table_name
)
-- ,'y' -- << uncomment to print commands only without execution
);
end loop;
end;
/

How to declare a cursor if table used in select statement may not exist sometimes?

I need to run below code on different environments and it works fine when table1 exists but when it does not exist then it throws error in cursor declaration that "table or view does not exist".
I am running this on Oracle.
Could you please help me in correcting this?
Thanks in advance.
DECLARE
CURSOR my_cursor IS (select "col1" from "table1");
name1 VARCHAR2(256);
tableCount NUMBER;
BEGIN
Select count(*) into tableCount from user_tab_cols where table_name = 'table1' and column_name = 'col2';
IF tableCount > 0 THEN
OPEN my_cursor;
LOOP
FETCH my_cursor into name1;
EXIT WHEN my_cursor%notfound;
-- Update or delete statement here
DBMS_OUTPUT.PUT_LINE('value is ' || name1);
END LOOP;
CLOSE my_cursor;
END IF;
END;
/
A CURSOR with a defined return type is strongly typed. Sys_refcursors are weakly typed. This means that any return type in a CURSOR must be valid. A SYS_REFCURSOR is more flexible and can be defined at a later time.
When setting a CURSOR in the declaration section, you must use a SQL statement that will execute properly. In this case, you are setting the CURSOR to select a column from a table that does not exist. The database execution will not reach the body of the code since it errors prior to exiting the declaration block.
To fix this, use a SYS_REFCURSOR with a dynamic sql query, as mentioned by Tejash above. This allows you to check to see if the table exists before setting the cursor. If the table exists, set the cursor to select from the specified table. If it doesn't, output a message saying that it does not exist.
Note that you can also use the SQL error codes, as shown in other answers. I prefer personally to handle business rules in the logic prior to the error occurring.
DECLARE
my_cursor sys_refcursor;
name1 VARCHAR2(256);
tableCount NUMBER;
BEGIN
Select count(*) into tableCount from user_tab_cols where table_name = 'table1' and column_name = 'col2';
IF tableCount > 0 THEN
OPEN my_cursor for 'select order_id from table1';
LOOP
FETCH my_cursor into name1;
EXIT WHEN my_cursor%notfound;
-- Update or delete statement here
DBMS_OUTPUT.PUT_LINE('value is ' || name1);
END LOOP;
CLOSE my_cursor;
else
dbms_output.put_line('Table does not exist');
END IF;
END;
http://docs.oracle.com/database/122/LNPLS/static-sql.htm#LNPLS568
You can use the exceptions and dynamic string for a cursor as follows:
DECLARE
MY_CURSOR SYS_REFCURSOR;
NAME1 VARCHAR2(256);
TABLECOUNT NUMBER;
BEGIN
OPEN MY_CURSOR FOR 'SELECT ACC_NR FROM ACCOUNT'; -- dynamic string for cursor
LOOP
FETCH MY_CURSOR INTO NAME1;
EXIT WHEN MY_CURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('value is ' || NAME1);
END LOOP;
CLOSE MY_CURSOR;
EXCEPTION -- exception handling using SQLCODE
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_OUTPUT.PUT_LINE('Table does not exists');
ELSIF SQLCODE = -904 THEN
DBMS_OUTPUT.PUT_LINE('Invalid column name');
ELSE
DBMS_OUTPUT.PUT_LINE('Other error');
END IF;
END;
/

How to write Execute immediate in cursor select Query

How to wite EXECUTE IMMEDIATE in the cursor select Query.
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
CURSOR cur_attendee_data
IS
EXECUTE IMMEDIATE l_cur_query;
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
here I try to use initialize cursor outside begin block but it rises exception, how to write a dynamic query for the cursor,
cur_attendee_data is
EXECUTE IMMEDIATE l_cur_query;
Error(113,8): PLS-00103: Encountered the symbol "CUR_ATTENDEE_DATA" when expecting one of the following: := . ( # % ;
If you fetch all rows with BULK COLLECT INTO ... then you need only one loop, the second loop is useless.
The basic solution would be this one:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from {table X} where c1=:d';
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
However, I don't see any reason to make dynamic SQL. You can simply run
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR SELECT * from {table X} where c1 = in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
You can use below code instead -
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (100) := 'SELECT * from table X where c1=:in_from_date';
TYPE t_cur IS REF CURSOR;
cur_attendee_data t_cur
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
You need to use open cursor for '' as following:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
cur_attendee_data SYS_REFCURSOR; -- JUST DECLARED THE CURSOR
--TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
-- INDEX BY PLS_INTEGER; -- this declaration must be at schema level
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data for l_cur_query; -- OPEN THE CURSOR WITH DYNAMIC QUERY
..
.. -- YOUR CODE AS IT IS
..
Cheers!!

How can i execute function?

I am trying to make a function that returns a row based upon the number i'm giving. I have created the function but can't seem to make it work.
This is my function:
create or replace function vitest(t_na test.na%type)
return sys_refcursor is t_test sys_refcursor;
begin
open t_test for
select * from test where na = t_na;
return t_test;
end;
I have tried using:
select vitest(1) from dual;
But it gives me error: ORA-00932.
I also tried using:
begin
vitest(1);
end;
But it says vitest is not a procedure...
How can i make it work?
Assuming your test table has col1,col2,col3 columns, you could call your function as follows:
DECLARE
l_cursor SYS_REFCURSOR;
l_col1 test.col1%TYPE;
l_col2 test.col2%TYPE;
l_col3 test.col3%TYPE;
BEGIN
l_cursor := vitest(1);
LOOP
FETCH l_cursor
INTO l_col1, l_col2, l_col3;
EXIT WHEN l_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(l_col1 || ' | ' || l_col2 || ' | ' || l_col3);
END LOOP;
CLOSE l_cursor;
END;
*Also, your select now would change
from
select * from test where na = t_na;
to
select col1,col2,col3 from test where na = t_na;