views:

823

answers:

4

Hi.

I'm working on a pl sql stored procedure. What I need is to do a select, use a cursor and for every record build a string using values. At the end I need to write this into a file. I try to use dbms_output.put_line("toto") but the buffer size is to small because I have about 14 millions lines. I call my procedure from a unix ksh. I'm thinking at something like using "spool on" (on the ksh side) to dump the result of my procedure, but I don' know how to do it (if this is possible)

Anyone has any idea?

Thank alot.

C.C.

A: 

utl_file is your friend http://www.adp-gmbh.ch/ora/plsql/utl_file.html But is writes the data to the filesystem on the server so you probably need your DBA's help for this.

Robert Merkwürdigeliebe
I saw the utl_file but I cannot use it because I don't have the rights.
CC
+3  A: 

Unless it is really necessary, I would not use a procedure.

If you call the script using SQL*Plus, just put the following into your test.sql (the SETs are from SQL*Plus FAQ to remove noise):

SET ECHO OFF
SET NEWPAGE 0
SET SPACE 0
SET PAGESIZE 0
SET FEEDBACK OFF
SET HEADING OFF
SET TRIMSPOOL ON
SET TAB OFF

Select owner || ';' || object_name
From all_objects;

QUIT

and redirect output to a file (test.txt):

sqlplus user/passwd@instance @ test.sql > test.txt

If you really need to do stuff in PL/SQL, consider putting that into a function and call it per record:

Create Or Replace Function calculate_my_row( in_some_data In Varchar2 )
  Return Varchar2
As
Begin
  Return in_some_data || 'something-complicated';
End calculate_my_row;

Call:

Select owner || ';' || calculate_my_row( object_name )
From all_objects;

Performance could suffer, but it should work. Make sure, that what you try can't be done in pure SQL, though.


Reading your comment I think that Analytic Function Lag is what you need.

This example appends * in case the value of val has changed:

With x As (
      Select 1 id, 'A' val FROM dual
Union Select 2 id, 'A' val FROM dual
Union Select 3 id, 'B' val FROM dual
Union Select 4 id, 'B' val FROM dual
)
--# End of test-data
Select
  id,
  val,
  Case When ( val <> prev_val Or prev_val Is Null ) Then '*' End As changed
From (
  Select id, val, Lag( val ) Over ( Order By id ) As prev_val
  From x
)
Order By id

Returns

        ID V C
---------- - -
         1 A *
         2 A  
         3 B *
         4 B  
Peter Lang
This does not work also, because the problem is that I need to do some stuff for every row, so I did a cursor and inside I do my stuff for every line.
CC
Please see my updated answer. What are you trying that can't be done in pure SQL?
Peter Lang
Not bad this solution, but...:)The treatement I do on each line is:If the before value of a field is different that the current value, I will concatenate something, else do nothing.So using a cursor it works, but I cannot/ I don't know how to dump the result.
CC
Please see my next update using Analytic Function `Lag`.
Peter Lang
Interesting solution but I have 14 millions lines, and I'm trying to do something in a reasonable time.I cannot afford to do 4 times the select.
CC
Not sure what you mean? My 4 selects are only to provide some test-data. Remove the part before `End of test-data` and select from your table. Performance should actually be a lot better than with your current solution.
Peter Lang
A: 

Tom Kyte has answered this, see

Flat

from this question on Ask Tom

Paul James
+1  A: 

If every line of your output is the result of an operation on one row in the table, then a stored function, combined with Peter Lang's answer, can do what you need.

create function create_string(p_foobar foobar%rowtype) return varchar2 as
begin
  do_some_stuff(p_foobar);
  return p_foobar.foo || ';' ||p_foobar.bar;
end;
/

If it is more complicated than that, maybe you can use a pipelined table function

create type varchar_array
    as table of varchar2(2000)
/

create function output_pipelined return varchar_array PIPELINED as
  v_line varchar2(2000);
begin
  for r_foobar in (select * from foobar)
  loop
    v_line := create_string(r_foobar);
    pipe row(v_line);
  end loop;
  return;
end;
/ 

select * from TABLE(output_pipelined);  
ammoQ
I'm trying this solution, but I since I have huge amount of data, can I use it?v_line will have more that 2000 caracters? Does it still work? Or in v_line is only the infromation of one line ?
CC
v_line is the information for one line.
ammoQ
Great. It works. Thanks alot.
CC