How can I compile PL/SQL source saved in table? - oracle

How do I compile PL/SQL source code that I currently have saved in an Oracle table? (I'm making a copy of the source code from the USER_SOURCE view, then I'm deleting those objects and want to restore from my saved copy.)
I'm sure there is an easy way, but I'm just not entering the right search terms.

Try this:
declare
text varchar2(4000);
begin
select code into text from bkp_table;
execute immediate 'create or replace ' || text;
end;
/
Ok this works if all lines of code are stored in single line. If you want to execute code that is stored in multiple lines you should go for something like:
declare
text varchar2(32767);
begin
select listagg(text, ' ') within group (order by line) into text from all_source where name = 'MYPROC';
execute immediate 'create or replace ' || text;
end;
/
Problem starts when 32767 characters is too few. In such case this may be a solution:
declare
text clob;
begin
for x in (select text from all_source where name = 'LONGTEST') loop
text := text || x.text;
end loop;
execute immediate 'create or replace ' || text;
end;
/
Please also have a look on that why it is a bit odd thing.
EDIT
As suggested changed to dbms_lob and in that case clob needs to be initialised:
declare
text clob := ' ';
begin
for x in (select text from all_source where name = 'LONGTEST') loop
dbms_lob.append(text, x.text);
end loop;
execute immediate 'create or replace ' || text;
end;
/

Related

Oracle pl/sql Array

let's see if somebody can help me, I need to delete rows from different tables and I did think to do it using an array so i wrote this :
DECLARE
TYPE mytype_a IS TABLE OF VARCHAR2(32) INDEX BY BINARY_INTEGER;
mytype mytype_a;
BEGIN
mytype(mytype.count + 1) := 'MYTABLE';
FOR i IN 1 .. mytype.count
LOOP
DELETE mytype(i) WHERE valid = 'N';
END LOOP;
END;
Trying to run this piece of code using sqldeveloper I get the ORA-00933 command not properly ended, if I put directly the table name it works, what am I doing wrong?
Thank you.
Thank you very much guys, it works perfectly.
This is not the correct approach. You have to use Dynamic SQL for this -
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar(500) := NULL;
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid =''N''';
EXECUTE IMMEDIATE stmt;
end loop;
END;
You would need to use dynamic SQL, concatenating the table name from the collection into the statement, inside your loop:
execute immediate 'DELETE FROM ' || mytype(i) || ' where valid = ''N''';
Or you can put the statement into a variable so you can display it for debugging purposes, and then execute that, optionally with a bind variable for the valid value:
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
... which I've made also display how many rows were deleted from each table. Note though that you shoudln't rely on the caller being able to see anything printed with dbms_output - it's up to the client whether it shows it.
The whole anonymous block would then be:
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar2(4000);
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
end loop;
END;
/
You could use a built-in collection type to simplify it even further.
db<>fiddle showing some options.
Hopefully this doesn't apply, but if you might have any tables with quoted identifiers then you would need to add quotes in the dynamic statement, e.g.:
stmt := 'DELETE FROM "' || mytype(i) || '" where valid = :valid';
... and make sure the table name values in your collection exactly match the names as they appear in the data dictionary (user_tables.table_name).

Procedure Results with Variable Column Alias - PLSQL/Oracle

I've got this procedure working in TOAD/PLSQL but, would like the alias for the first column to be set to the field_name argument passed to the procedure. I think I need to build the query as a string like,
query := 'Select 1 as ' || field_name || ' From Dual';
But am not getting it right. Is what I have in mind possible?
Thanks and the working code I'm trying to modify is below.
Create or Replace Procedure Delete_Me(field_name NVarChar2)
as
result_set sys_refcursor;
BEGIN
open result_set for
Select
Elapsed_Time((Select Start_Time From Temp_Time1)) as field_name
,To_Char(SysDate, 'HH12:MI:SS AM') as Time_Completed
,Elapsed_Time((Select Start_Time From Temp_Time0)) as Process_So_Far
From
Dual;
DBMS_SQL.RETURN_RESULT(result_set);
End;
After comment:
I pass the procedure a string and its valued is placed in, "field_name." I would like the alias of the first column to adopt the value of field_name. So if I call the procedure thusly:
BEGIN
DeleteMe('Random_Column_Name');
END;
The first column name would be called, "Random_Column Name." If I called the procedure this way:
BEGIN
DeleteMe('Different_Column_Name');
END;
The first column would be names, "Different_Column_Name."
After Dmitry's second comment:
It doesn't mean anything. It's an example of what I've tried and failed to get to work.
I understand that you need to make a dynamic query, the way to do that is sometihing like this:
DECLARE
TYPE ty_refcur IS REF CURSOR;
c_mycur ty_refcur;
whatever varchar2(200) := 'whatever';
my_query varchar2(500);
BEGIN
my_query := 'Select ''hello''as '||whatever||' from dual';
OPEN c_mycur FOR my_query;
--whatever you want to do
CLOSE c_mycur;
END;
Here's what I finally came up with. Stanimir helped me understand how variables are used. Thanks for that!
Create or Replace Procedure Report_Elapsed_Time(field_name0 NVarChar2,
field_name1 NVarChar2)
as
result_set sys_refcursor;
query VarChar2(30000);
BEGIN
query := 'Select
Elapsed_Time((Select Start_Time From Temp_Time1)) as ' || Replace(field_name0, ' ', '_') || '
,To_Char(SysDate, ''HH12:MI:SS AM'') as Time_Completed
,Elapsed_Time((Select Start_Time From Temp_Time0)) as ' || Replace(field_name1, ' ', '_') || '
From
Dual';
open result_set for query;
DBMS_SQL.RETURN_RESULT(result_set);
End;

how to use "like" command in below query

I am importing text file through SQL*Loader into an Oracle table but i don't want to give the specific name of the file, I want to import only the .txt file extensions file.
look the below code :
create or replace
PROCEDURE EXT_TABLE
AS
A1 NUMBER ;
L_QUERY VARCHAR2(1000) := NULL;
L_DROP VARCHAR2(10000) := NULL;
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE IMPORT_TEST
( EMP_ID NUMBER (10)
)
ORGANIZATION EXTERNAL
( TYPE ORACLE_LOADER
DEFAULT DIRECTORY IMPORT
ACCESS PARAMETERS
( RECORDS DELIMITED BY NEWLINE
FIELDS TERMINATED BY '',''
MISSING FIELD VALUES ARE NULL
)
LOCATION ('file with .txt extension')
)reject limit unlimited';
L_QUERY:= 'INSERT INTO MPRN SELECT * FROM IMPORT_TEST';
EXECUTE IMMEDIATE L_QUERY;
L_DROP := 'drop table IMPORT_TEST ';
execute immediate L_DROP;
--execute immediate 'DROP IMPORT_TEST';
commit;
END EXT_TABLE;
At the location, LOCATION ('file with .txt extension'), I don't want to give the name of the file as in the directory only one txt file is there. I don't want to use the IN parameter. I want to search from the directory only. The user will run the procedure and it will import the txt file automatically without selecting manually.
For the most part you aren't going to be able to do this in a pure PL/SQL fashion. There is a workaround listed here: Listing files in a specified directory using PL/SQL but considering the requirement for SYS that may not be exactly what you are looking for. After that a Java Stored Procedure would be your best bet.
If you are able to determine the filename, you can redefine the location for your external table on the fly with an execute immediate call. You could put it in a procedure like this and make use of it before querying your external table:
procedure alterExtTableFileName(a_tableName varchar2, a_filename varchar2) is
pragma autonomous_transaction;
begin
dbms_output.put_line('alterExtTableFileName(TableName=' || a_tableName || ' FileName=' || a_filename || ')');
execute immediate 'alter table ' || a_tableName || ' LOCATION (''' || a_filename || ''')';
commit;
exception when others then
rollback;
raise;
end alterExtTableFileName;

How to fetch values from cursor into variables?

I have problems fetching values from cursor into variables:
create or replace
procedure projectinfo(num clubs.clubid%type)
as
--identify variables
p_cln clubs.clubname%type;
p_projn projects.projectname%type;
p_projnum number;
p_taskn tasks.taskname%type;
p_tasknum number;
cursor cur is
select c.clubname, p.projectname, t.taskname
from clubs c join projects p on c.clubid=p.clubid
join tasks t on t.projectid=p.projectid
where c.clubid=num;
--I have checked the above cursor and it's worked fine!!!
begin
p_projnum:=0;
p_tasknum:=0;
open cur;
loop
fetch cur into p_cln,p_projn, p_taskn;
dbms_output.put_line(p_cln|| ' ' ||p_projn|| ' ' || p_taskn);
-- the above command does not print variable values!!!
exit when cur%notfound;
p_projnum:=p_projnum+1;
dbms_output.put_line(' ************************ ');
dbms_output.put_line(' club name : ' ||p_cln);
dbms_output.put_line( ' project ' ||p_projnum|| ' ' || p_projn);
loop
p_tasknum:=p_tasknum+1;
dbms_output.put_line('Task: ' ||p_tasknum|| ' ' || p_taskn);
fetch cur into p_cln,p_projn, p_taskn;
exit when cur%notfound;
end loop;
end loop;
close cur;
end projectinfo;
I have checked my cursor and itdoes contain all values that I need. My programm compiles FINE but does not print any output!!!
What tool are you using to run your procedure? By default, most tools will not allocate a buffer for dbms_output to write to and will not display anything written to dbms_output. That's why you'd never depend on dbms_output for any real code.
If you are using SQL*Plus, you need to enable serveroutput before executing your procedure
SQL> set serveroutput on;
SQL> exec projectinfo( <<some number>> );
If you are using a GUI, the GUI will almost certainly have a way to enable dbms_output. But that will be very different for different applications. In SQL Developer, for example, you'd need to ensure that the DBMS Output window is visible, click the green "plus sign" icon, and choose your connection to enable dbms_output.

Form dynamic query and execute in 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.

Resources