I have a procedure that converts the result of a specific query in a CSV File which is saved in a directory in the server. My question is that is there any way that i can generate unique names for the file every time i save it?
try this
-- not tested
select to_char( sysdate,'yyyymmddhh24miss' ) || '.txt' File_nm from dual;
spool &File_nm ;
-- run query
spool off;
If you create a SEQUENCE, you can increment the sequence each time you go to write your file.
DECLARE
l_filename VARCHAR2 (200);
BEGIN
SELECT 'somename_' || seq_name.NEXTVAL INTO l_filename FROM DUAL;
END;
Related
I know that in order to print something on sqlplus like below:
begin
dbms_output.put_line('Hello!');
end;
/
I need to call
set serveroutput on;
before that.
I also know that is not needed, but I can also call
DBMS_OUTPUT.enable;
before, just in case. This is working for me.
But what if I want to keep printing the progress of a long loop? It seems impossible to me. I've tried everything to print some progress on the loop below but just doesn't work. Is there some way of doing that? I even tried to spool to a file and didn't work.
Note 1: I can't truncate or partition this table as the DBA doesn't want to help me with that, so I have to use this nasty loop...
Note 2: I've noticed that once the loop is done, the whole output is printed. Looks like oracle is buffering the output and printing everything at the end. I'm not sure how to avoid that and print on every loop iteration.
set serveroutput on;
declare
e number;
i number;
nCount number;
f number;
begin
DBMS_OUTPUT.enable;
dbms_output.put_line('Hello!');
select count(*) into e from my_big_table where upd_dt < to_date(sysdate-64);
f :=trunc(e/10000)+1;
for i in 1..f
loop
delete from my_big_table where upd_dt < to_date(sysdate-64) and rownum<=10000;
commit;
DBMS_OUTPUT.PUT_LINE('Progress: ' || to_char(i) || ' out of ' || to_char(f));
end loop;
end;
Thank you for any answer.
There are 2 standard ways for such things:
set module and action in your session DBMS_APPLICATION_INFO.SET_MODULE:
SQL> exec DBMS_APPLICATION_INFO.SET_MODULE('my_long_process', '1 from 100');
PL/SQL procedure successfully completed.
SQL> select action from v$session where module='my_long_process';
ACTION
----------------------------------------------------------------
1 from 100
set session_longops:
DBMS_APPLICATION_INFO.SET_SESSION_LONGOPS
I'd recommend it in your case since that is exactly designed for long operations.
Example on Oracle-Base.
----
PS: dbms_output,put_line saves all output in a collection (nested table) variable of dbms_output package, so you can't get it from another session and client can't get it during user call (execution). In addition to set serveroutput on you can also get the output using dbms_output.get_lines: http://orasql.org/2017/12/10/sqlplus-tips-8-dbms_output-without-serveroutput-on/
Btw, in case if you need to filter or analyze output from dbms_output, sometimes it's convenient to get output in a query, so you can use filter strings in where clause or aggregate them: https://gist.github.com/xtender/aa12b537d3884f4ba82eb37db1c93c25
DBMS_OUTPUT will only ever be displayed after the PL/SQL code has terminated and control has returned to the calling program.
Output is, as you found, buffered. When your PL/SQL code finishes, then the calling program (e.g. SQL*Plus) can go and fetch that output.
Insert into another table, maybe call it "MYOUTPUT".
Create the table:
create table myoutput (lineno number, outline varchar2(80));
Add this after your delete:
insert into MYOUTPUT values (i,'Progress: ' || to_char(i) || ' out of ' || to_char(f));
Then select from MYOUTPUT periodically to see progress.
select outline from myoutput order by lineno;
Bobby
You can use UTL_FILE to write output to an external file, as in:
DECLARE
fh UTL_FILE.FILE_TYPE;
nRow_count NUMBER := 0;
BEGIN
fh := UTL_FILE.FOPEN('DIRECTORY_NAME', 'some_file.txt', 'w');
FOR aRow IN (SELECT *
FROM SOME_TABLE)
LOOP
nRow_count := nRow_count + 1;
IF nRow_count MOD 1000 = 0 THEN
UTL_FILE.PUT_LINE(fh, 'Processing row ' || nRow_count);
UTL_FILE.FFLUSH(fh);
END IF;
-- Do something useful with the data in aRow
END LOOP; -- aRow
UTL_FILE.FCLOSE_ALL; -- Close all open file handles, including
-- the ones I've forgotten about...
END;
Apologies for the newbie question, I am writing an Oracle stored procedure that opens a cursor for a specific SQL, calculates some variables for each row returned by the cursor but the stored procedure should return as a result set these variables that have been calculated for each row returned by the cursor. I am a bit confused on how to do this - can anyone help?!
I did read some of it so far I have something like this (just a trimmed down example and not exact code) but just need to return v_calc and v_calc_res in a result set:-
CREATE OR REPLACE procedure sp_test
(
in_input in number,
out_return out sys_refcursor
)
as
v_calc number;
v_calc_res number;
CURSOR C_test IS
select blah from test where blah = in_input;
begin
open c_test
loop
fetch c_test into v_calc;
v_calc_res := v_calc*5;
end loop;
end;
If you want a procedure to return a reference cursor for the calling routine to consume the procedure itself cannot then consume it. Cursors, including reference cursors are 1 way, 1 time consumables. As for as the desired calculations, they can be added to select defined for the cursor. So:
-- setup
create table test (blah integer, blah_stuff varchar2(50) );
-- build sp
create or replace procedure sp_blah_text(
in_input in number
, out_cur out sys_refcursor
)
is
begin
open out_cur for
select blah, blah_stuff, blah*5 as blah_x_5
from test
where blah = in_input;
end sp_blah_text;
-- test data
insert into test(blah, blah_stuff)
select 1,'a' from dual union all
select 2,'b' from dual union all
select 2,'x' from dual union all
select 2,'z' from dual union all
select 3,'c' from dual;
-- test
declare
ref_cur sys_refcursor;
l_blah test.blah%type;
l_stuff test.blah_stuff%type;
l_blah_5 test.blah%type;
begin
dbms_output.enable(null);
sp_blah_text(2,ref_cur);
loop
fetch ref_cur
into l_blah
, l_stuff
, l_blah_5;
exit when ref_cur%notfound;
dbms_output.put_line('blah=' || l_blah || ',stuff=' || l_stuff || ',blah*5=' || l_blah_5);
end loop;
end;
This works a treat thank you very much. I now have a performance issue that maybe you could help with. When I open the cursor, I then run several other SELECT statements to retrieve values using the variables from the cursor (see below). I assume this is because the switch between PL/SQL and SQL engine. Would using table collections help? But as I see since I need different columns from different tables I would need to have several different collections, how could I output everything in one record?
CREATE OR REPLACE procedure sp_test
(
in_input in number
)
as
v_calc number;
v_calc_res number;
v_blah_blah number;
v_blah_blah_blah number;
v_blah_blah_blah number;
CURSOR C_test IS
select blah from test where blah = in_input;
begin
open c_test
loop
fetch c_test into v_calc;
select blah_blah into v_blah_blah from t_blah_blah;
select blah_blah_blah into v_blah_blah_blah from t_blah_blah_blah;
select blah_blah_blah_blah into v_blah_blah_blah_blah from t_blah_blah_blah_blah;
v_calc_res := v_calc*5*v_blah_blah*v_blah_blah_blah*v_blah_blah_blah_blah
end loop;
end;
I have to call oracle SQL statement inside my project.
All connection related stuff is done, but my tool does not capture the output parameter executed by oracle.
Hence I need alter this query to return p_num value in a select statement.
i.e. the table which has 1 column ('p_num') with column name called 'Result' and which has only one row which is p_num value.
Following is the sql statement which currently gives output value with help of dbms_output.put_line
DECLARE
p_num varchar2(4000);
message varchar2(4000) ;
BEGIN
p_num := MyFunction();
dbms_output.put_line('Message : ' || p_num) ;
END;
What I want is p_num value in a SELECT statement so that I can capture specific column inside my bpm tool.
You can directly call the function in the SELECT statement.
1) First way is to do it VIA plain SQL
SELECT MyFunction FROM DUAL;
2) Second way is PLSQL but i will not recommend it unless its unavoidable
set serveroutput on;
declare
lv_var VARCHAR2(100);
lv_out_param VARCHAR2(100);
BEGIN
lv_var:=MyFunction(lv_out_param);
dbms_output.put_line(lv_var||' '||lv_out_param);
END;
/
See the sample Timesten procedure below.
CREATE OR REPLACE PROCEDURE test_proc(employee_id IN NUMBER) AS
salary NUMBER;
BEGIN
SELECT emp_sal INTO salary FROM employee where emp_id = employee_id;
DBMS_OUTPUT.PUT_LINE('Employee Id:' || employee_id || ' Annual Income:' || salary*12);
END;
/
If I call the procedure from Command line interface(ttisql), dbms_output.put_line logs gets printed there only.
But I want to collect such debug logs to somewhere else in a log file. Whenever procedure get executed it should append these content to a file. Is there any possible way to do that?
If you are calling your procedure from ttisql as you've said you can use spool filepath and spool off to log any messages appeared on the screen. To append new information to the already existed log file just use APPEND option after filepath
spool c:\logfile.log append
I need to debug in pl/sql to figure times of procedures, I want to use:
SELECT systimestamp FROM dual INTO time_db;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);
but I don't understand where the output goes to and how can I redirect it to a log file that will contain all the data I want to collect?
DBMS_OUTPUT is not the best tool to debug, since most environments don't use it natively. If you want to capture the output of DBMS_OUTPUT however, you would simply use the DBMS_OUTPUT.get_line procedure.
Here is a small example:
SQL> create directory tmp as '/tmp/';
Directory created
SQL> CREATE OR REPLACE PROCEDURE write_log AS
2 l_line VARCHAR2(255);
3 l_done NUMBER;
4 l_file utl_file.file_type;
5 BEGIN
6 l_file := utl_file.fopen('TMP', 'foo.log', 'A');
7 LOOP
8 EXIT WHEN l_done = 1;
9 dbms_output.get_line(l_line, l_done);
10 utl_file.put_line(l_file, l_line);
11 END LOOP;
12 utl_file.fflush(l_file);
13 utl_file.fclose(l_file);
14 END write_log;
15 /
Procedure created
SQL> BEGIN
2 dbms_output.enable(100000);
3 -- write something to DBMS_OUTPUT
4 dbms_output.put_line('this is a test');
5 -- write the content of the buffer to a file
6 write_log;
7 END;
8 /
PL/SQL procedure successfully completed
SQL> host cat /tmp/foo.log
this is a test
As an alternative to writing to a file, how about writing to a table? Instead of calling DBMS_OUTPUT.PUT_LINE you could call your own DEBUG.OUTPUT procedure something like:
procedure output (p_text varchar2) is
pragma autonomous_transaction;
begin
if g_debugging then
insert into debug_messages (username, datetime, text)
values (user, sysdate, p_text);
commit;
end if;
end;
The use of an autonomous transaction allows you to retain debug messages produced from transactions that get rolled back (e.g. after an exception is raised), as would happen if you were using a file.
The g_debugging boolean variable is a package variable that can be defaulted to false and set to true when debug output is required.
Of course, you need to manage that table so that it doesn't grow forever! One way would be a job that runs nightly/weekly and deletes any debug messages that are "old".
use
set serveroutput on;
for example:
set serveroutput on;
DECLARE
x NUMBER;
BEGIN
x := 72600;
dbms_output.put_line('The variable X = '); dbms_output.put_line(x);
END;
If you are just testing your PL/SQL in SQL Plus you can direct it to a file like this:
spool output.txt
set serveroutput on
begin
SELECT systimestamp FROM dual INTO time_db;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);
end;
/
spool off
IDEs like Toad and SQL Developer can capture the output in other ways, but I'm not familiar with how.
In addition to Tony's answer, if you are looking to find out where your PL/SQL program is spending it's time, it is also worth checking out this part of the Oracle PL/SQL documentation.
Using UTL_FILE instead of DBMS_OUTPUT will redirect output to a file:
http://oreilly.com/catalog/oraclebip/chapter/ch06.html
As a side note, remember that all this output is generated in the server side.
Using DBMS_OUTPUT, the text is generated in the server while it executes your query and stored in a buffer. It is then redirected to your client app when the server finishes the query data retrieval. That is, you only get this info when the query ends.
With UTL_FILE all the information logged will be stored in a file in the server. When the execution finishes you will have to navigate to this file to get the information.
Hope this helps.
Its possible write a file directly to the DB server that hosts your database, and that will change all along with the execution of your PL/SQL program.
This uses the Oracle directory TMP_DIR; you have to declare it, and create the below procedure:
CREATE OR REPLACE PROCEDURE write_log(p_log varchar2)
-- file mode; thisrequires
--- CREATE OR REPLACE DIRECTORY TMP_DIR as '/directory/where/oracle/can/write/on/DB_server/';
AS
l_file utl_file.file_type;
BEGIN
l_file := utl_file.fopen('TMP_DIR', 'my_output.log', 'A');
utl_file.put_line(l_file, p_log);
utl_file.fflush(l_file);
utl_file.fclose(l_file);
END write_log;
/
Here is how to use it:
1) Launch this from your SQL*PLUS client:
BEGIN
write_log('this is a test');
for i in 1..100 loop
DBMS_LOCK.sleep(1);
write_log('iter=' || i);
end loop;
write_log('test complete');
END;
/
2) on the database server, open a shell and
tail -f -n500 /directory/where/oracle/can/write/on/DB_server/my_output.log
An old thread, but there is another alternative.
Since 9i you can use pipelined table function.
First, create a type as a table of varchar:
CREATE TYPE t_string_max IS TABLE OF VARCHAR2(32767);
Second, wrap your code in a pipelined function declaration:
CREATE FUNCTION fn_foo (bar VARCHAR2) -- your params
RETURN t_string_max PIPELINED IS
-- your vars
BEGIN
-- your code
END;
/
Replace all DBMS_OUTPUT.PUT_LINE for PIPE ROW.
Finally, call it like this:
SELECT * FROM TABLE(fn_foo('param'));
Hope it helps.
Try This:
SELECT systimestamp INTO time_db FROM dual ;
DBMS_OUTPUT.PUT_LINE('time before procedure ' || time_db);