Home>Question Details



BELBAHRI -- Thanks for the question regarding "extracting data to a flat file.", version

Submitted on 2-May-2000 13:20 Central time zone
Last updated 6-Aug-2009 8:45

You Asked

How can i extract data from flat file to oracle table 
with plsql ?
 

and we said...

I'd probably use sqlplus myself (see;
http://asktom.oracle.com/~tkyte/flat/index.html
for some tools).

PL/SQL can do it, it might look like:

create or replace function  dump_csv( p_query     in varchar2,
                                      p_separator in varchar2 
                                                    default ',',
                                      p_dir       in varchar2 ,
                                      p_filename  in varchar2 )
return number
AUTHID CURRENT_USER
is
    l_output        utl_file.file_type;
    l_theCursor     integer default dbms_sql.open_cursor;
    l_columnValue   varchar2(2000);
    l_status        integer;
    l_colCnt        number default 0;
    l_separator     varchar2(10) default '';
    l_cnt           number default 0;
begin
    l_output := utl_file.fopen( p_dir, p_filename, 'w' );

    dbms_sql.parse(  l_theCursor,  p_query, dbms_sql.native );

    for i in 1 .. 255 loop
        begin
            dbms_sql.define_column( l_theCursor, i, 
                                    l_columnValue, 2000 );
            l_colCnt := i;
        exception
            when others then
                if ( sqlcode = -1007 ) then exit;
                else
                    raise;
                end if;
        end;
    end loop;

    dbms_sql.define_column( l_theCursor, 1, l_columnValue, 
                            2000 );

    l_status := dbms_sql.execute(l_theCursor);

    loop
        exit when ( dbms_sql.fetch_rows(l_theCursor) <= 0 );
        l_separator := '';
        for i in 1 .. l_colCnt loop
            dbms_sql.column_value( l_theCursor, i, 
                                   l_columnValue );
            utl_file.put( l_output, l_separator ||  
                                    l_columnValue );
            l_separator := p_separator;
        end loop;
        utl_file.new_line( l_output );
        l_cnt := l_cnt+1;
    end loop;
    dbms_sql.close_cursor(l_theCursor);

    utl_file.fclose( l_output );
    return l_cnt;
end dump_csv;
/


You would use that for example like this:

create or replace procedure test_dump_csv
as
    l_rows  number;
begin
    l_rows := dump_csv( 'select * 
                           from all_users 
                           where rownum < 25', 
                        ',', '/tmp', 'test.dat' );
end;
/


Make sure to read about the INIT.ORA parameters (utl_file_dir) you need to setup !!!
 

Reviews    
3 stars Do the PL/SQL one dump Date type with time part?   March 29, 2002 - 2pm Central time zone
Reviewer: Connie from ON. Canada
I am not sure if the PL/SQL procedure dump date type with time part? For example, if there is a 
date column starttime, what the result of that field in the output flat file?

Why do you use the line as: 
dbms_sql.define_column( l_theCursor, 1, l_columnValue, 2000); 
since you have already define columns in the loop above. 

Thx, 


Followup   March 29, 2002 - 2pm Central time zone:

just issue:

alter session set nls_date_format = 'dd-mon-yyyy hh24:mi:ss'

before (or during) the execution of this routine.  Then dates will come out with the time. 

5 stars Tom, this routine   April 14, 2002 - 4pm Central time zone
Reviewer: Kulguru 
writes to the flat file on the server side

my requirement right now is to write to the flat file on the client side,

will it suffice to remove all the utl_file calls with text_io package

Further, your routine cannot handle 'select * from emp', we have to specifically mention columns in 
the select.

Do you have a routine which accepts select * from emp.

Thank you 


Followup   April 14, 2002 - 4pm Central time zone:

TEXT_IO is only for forms.  So, unless you are doing this in forms -- you'll be changing the 
utl_file to dbms_output and hoping you are never asked to put out more then 255 bytes/row.

Further -- the routine can, does, and will handle select * quite well.  In fact -- the example 
provided uses select *. 

5 stars The good question to another question   April 15, 2002 - 5pm Central time zone
Reviewer: Andrew from calgary, Canada
Tom,

His/her question is spelled strangely. He/she wants to EXTRACT FROM FLAT FILE TO TABLE. (I.e. he 
wants to view beautiful sunset lokking towards the east) I.e. he/she wants to load.

Accordingly he/she has to modify your technique, opening file for reading, (not writing), parsing 
every line and executing sql to insert into table (instead of select).

Why bother with plsql. Just use sql*loader. 


Followup   April 16, 2002 - 7am Central time zone:

well, i have that one too

http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:464420312302

Guess I read the subject "extracting data to a flat file." on this one and answered that
.... 

5 stars Will sqlplus accept Text_io   April 16, 2002 - 12pm Central time zone
Reviewer: Famy 
1.Tom, sqlplus is also a client tool just like forms and reports, can I write a script using 
text_io and execute it from sqlplus.

2.Further, I have no access to the database server, so the utlfile pl/sql procedure and sqlldr 
solutions are out of questin.

What I want is a utility to write the data to the client side, and also a utilitythat will pick it 
up from the client side and dump it into a table in my schema.

Is this possible.
Kindly answer. 


Followup   April 16, 2002 - 9pm Central time zone:

1) no, sqlplus is NOT a client too "just like" forms and reports.  forms and reports actually have 
a full fledged PLSQL runtime engine in them.  SQLPlus does not.  TEXT_IO does not exist in sqlplus. 
 SPOOL does.
see

2) SPOOL  -- see

http://asktom.oracle.com/~tkyte/flat/index.html
for ideas.  As for ruling out sqlldr -- then you are basically "out of luck" -- or you are writing 
your OWN client program that parses an input and file loads it.

writing data to the client side -- trivial (see flat).
Loading it is trivial as well -- if you use sqlldr -- if you rule it out, it's hard. 

4 stars dump_csv   August 8, 2002 - 8am Central time zone
Reviewer: Vasaku 
Tom, I have modified your dump_csv using text_io, for using from forms.

Now..

I'm passing in the :system.last_query from forms, this also selects the rownum in the query .. so 
how should I filter out the rownum.

I also want to filter our all the commas where should I put the REPLACE.

And lastly I want to pass TAB as the separator, but when I passed chr(9)  as the separator it is 
not working?

Thanks 


Followup   August 8, 2002 - 9am Central time zone:

well, if you know which column rownum is (it is probably the FIRST or the LAST column right?? ) you 
can skip it.

What commas??? 

"it is not working?" -- neither is my car, can you fix it?  not working makes little sense to me.  
please please -- be more descriptive. 

5 stars different approach to write to flat file using text_io   August 8, 2002 - 8am Central time zone
Reviewer: Vasaku 
Tom,

Instead of using dump_csv(which uses a query as a input), if I want to just write the contents of 
the block(in forms) 
how can I do that.

Can you show a small example for an assumed example of a block containing 2 records and 2 columns.

Thx 


Followup   August 8, 2002 - 9am Central time zone:

You would use something like:


   go_block( 'block name' )
   first_record
   loop
       text_io to write the :block.item1, :block.item2, ... :block.itemN out
       next_record
       exit when no more records
   end loop


 

5 stars how to start writing from the secon or the 3rd line in the excel file   August 8, 2002 - 3pm Central time zone
Reviewer: Vasaku 
Tom

With the help of your program I'm able to successfully write to the csv file.

But I need column headings.That is I need to have column headings , and underneath it I need to 
write the data as per the user requirement.

Any idea how I can do it.

(Sorry for the above comment on 'not working' I got confused between column separator and line 
separator) 


Followup   August 9, 2002 - 7am Central time zone:

You can use describe_columns:

is
    l_desc_tab   dbms_sql.desc_tab;
    l_cols       number;
    l_cursor     number;
    l_query      long;
begin
    l_cursor := dbms_sql.open_cursor;
    dbms_sql.parse( l_cursor, p_query, dbms_sql.native );
    dbms_sql.describe_columns( l_cursor, l_cols, l_desc_tab );
    ....

to get an array of records that contain this data:

  type desc_rec is record (
        col_type            binary_integer := 0,
        col_max_len         binary_integer := 0,
        col_name            varchar2(32)   := '',
        col_name_len        binary_integer := 0,
        col_schema_name     varchar2(32)   := '',
        col_schema_name_len binary_integer := 0,
        col_precision       binary_integer := 0,
        col_scale           binary_integer := 0,
        col_charsetid       binary_integer := 0,
        col_charsetform     binary_integer := 0,
        col_null_ok         boolean        := TRUE);
  type desc_tab is table of desc_rec index by binary_integer;


See the Supplied Packages guide for the details.  That'll give you your column headings.


If you use our OWA_SYLK utility (search this site for 

owa_sylk utl_file

) you'll get column headings and lots more excell stuff easily.

 

5 stars how to avoid rowid , which is the first column   August 9, 2002 - 12pm Central time zone
Reviewer: Vasaku 
Tom 

1.can you tell me the way to change your dump_csv to ignore the first column i.e. the rowid.

2.If I want to capture the column names in the dump_csv , how should I do that , and where should I 
give the dbms_output.

(I cant use the owa_swlk, the DBA says it is not available) 


Followup   August 9, 2002 - 12pm Central time zone:

Umm, change 1 to 2 in the loop       
 
       for i in 2 .. l_colCnt loop
            dbms_sql.column_value( l_theCursor, i, 
                                   l_columnValue );
            utl_file.put( l_output, l_separator ||  
                                    l_columnValue );
            l_separator := p_separator;
        end loop;

so it loops over columns 2, 3, 4, .... 


Right above this comment -- I showed how to call dbms_sql.describe_columns.  You would do that 
after you parse.  you would then use utl_file/dbms_output/whatever to print out the column 
headings.


OWA_SYLK is as available as DUMP_CSV is.  OWA_SYLK is a utility posted on THIS SITE -- it is 
something I wrote (along with Chris Beck).  Search for it (like I said) and you'll find it. 

5 stars where did you code raise subprogram   August 20, 2002 - 9pm Central time zone
Reviewer: Sam 

What is the purpose of the below

exception
            when others then
                if ( sqlcode = -1007 ) then exit;
                else
                    raise;
                end if;
 and I dont see that you have coded the subprogram raise , which you are calling in the exception 
section.

Please explain. 


Followup   August 20, 2002 - 9pm Central time zone:

raise is part of the plsql language itself.  see the plsql guide. 

5 stars Extracting data to a flat file   April 10, 2003 - 4am Central time zone
Reviewer: Anil from Malaysia
Dear Tom,
I had successfully used dump_csv function to write to a text file. 

My code is like this
declare
l_rows number;
rpt_date date;
tran_date date;
begin
loop
select max(reportdate) into rpt_date from amlareport;
select max(txd_bus_dt) into tran_date from read.trandetail;
if tran_date > rpt_date
then rpt_date := rpt_date +1;
else exit;
end if;
l_rows := dump_csv('select * from read.trandetail where txd_bus_dt=(select max(reportdate)+1 from 
amlareport)','c:\oracle\admin\dwdev\udump','FD Report'||rpt_date);
insert into amlareport values ('FD PLACEMENT VIA CASH >= 250K',rpt_date);
commit;
exit when tran_date=rpt_date;
end loop;
end;
/

I want to have column headings in the text file.  Is there a need to change dump_csv function. 
Please tell me how and where the code should be changed.

Regards,

Anil
 


Followup   April 10, 2003 - 8am Central time zone:

Look up a page or two where we talk about this already. 

5 stars SQL output in a text file   April 26, 2003 - 10am Central time zone
Reviewer: TH from Manama, BAH
Dear Tom,

I need it in the reverse way.

What is the simple way to output the result of an SQL to a textfile. I will call that query from my 
application. I won't use SQL Plus.

I can use that feature for custom generated reports.
 


Followup   April 26, 2003 - 11am Central time zone:

what do you mean "in the reverse way".  this is all about outputting the results of a query to a 
file -- which seems to be what you are asking for. 

4 stars the dump_csv function and oracle forms   May 21, 2003 - 7am Central time zone
Reviewer: Marla from Philippines
hi, i tried to use the dump_csv function in Oracle Forms but when I compiled it, the following 
problems came out:

Encountered the symbol "AUTHID" when expecting one of the following:
.@%; is as character
The symbol ". was inserted before "AUTHID" to continue.

if i comment the AUTHID CURRENT_USER, the following error comes out:

Implementation Restriction: 'DBMS_SQL.NATIVE': cannot directly access remote
package variable or cursor

I was hoping to use the dump_csv function because this is what I need to do:


- Information entered in the current form will be saved in the database. (this works already)
- When button pressed trigger, extract data from the table (data of the current form w/c was just 
saved, primary key is sir_trans_no) into a text file delimited by ';'
- :SIR_TRANS_NO is the textbox that holds the primary key

This was supposed to be my trigger:

/* trigger WHEN-BUTTON-PRESSED */
DECLARE

1_row NUMBER;

BEGIN

1_row := dump_csv('select * from sir where sir_trans_no = :SIR_TRANS_NO','c:\temporary\requests', 
'REQUEST');

END;


can you help me please?
 


Followup   May 21, 2003 - 8am Central time zone:

that is because it is written for the database, not for forms.

authid current user is a database feature, makes no sense in forms.


You would want to use

EXEC_SQL in forms instead of DBMS_SQL.  
TEXT_IO in forms instead of UTL_FILE (to get the file on the client)

You need to take the concept but recode it to be a client side plsql implementation. 

5 stars Stored Procedure in DUMP_CSV   November 14, 2003 - 12pm Central time zone
Reviewer: wor from US
Hi Tom,
I have a many stored procedures which takes some inputs parameters and returns a refcursor.
I would like to dump the results of the refcusor into flat file using dump_csv function. But I 
notice that dump_csv takes only sql queries.
Instead of passing sql's I would like to call dump_csv with different stored procedures in a 
generic manner and based on the refcursor return values, the flat should be created. I DO NOT want 
to specify array variables and do a fetch into beacuse this removes the generic nature of the 
dump_csv functionality.
is this possible and how. 


Followup   November 15, 2003 - 8am Central time zone:

you cannot -- a ref cursor is not "procedurally processable" by PLSQL.

PLSQL cannot decribe it
PLSQL cannot dynamically fetch column1, then column2 then column3 from it

PLSQL would need to know AT COMPILE time exactly what the query looked like

so, you CANNOT use a refcursor in this case, you must use a DBMS_SQL "cursor" instead if you want 
this functionality. 

5 stars wor ( continue.. )   November 14, 2003 - 12pm Central time zone
Reviewer: wor from US
If the above solution is possible in pro*c than please tell me the pro*C way.I assume it will be 
faster in pro*C.

Thanks in advance,
Wor 


Followup   November 15, 2003 - 8am Central time zone:

see the first url above, it has a pro*c "flat" 

3 stars OK   January 9, 2004 - 12am Central time zone
Reviewer: Siva from Seattle,USA
Dear tom,
csv -> what does it stand for?
Bye! 


Followup   January 9, 2004 - 8am Central time zone:

comma separated values 

4 stars Extracting Data to a unspecified directory   February 4, 2004 - 4pm Central time zone
Reviewer: Scott Pedersen from New Zealand
Hi Tom,

I am currently working on a system where the user will need to take a dump of the data produced in 
Oracle Forms and save it as an Excel file.

I have read about the examples you have provided and it is just what I am looking for, the only 
thing is having to stipulate a directory to write to using UTL_FILE_DIR in the init.ora file, which 
from my understanding allows the user to save a file to a directory on the Oracle Server.

What I would like them to be able to do is save the file to a directory of their choosing on their 
workstation and not on the server. Is this possible?

Thanks in advance.

Regards,

Scott. 


Followup   February 4, 2004 - 6pm Central time zone:

use TEXT_IO, that writes on the "client" 

3 stars To create xls documents easy   February 5, 2004 - 6am Central time zone
Reviewer: Peter Visscher from The Netherlands
Hi Tom,

I know a nice way to make Excel sheets from sqlplus. You can use the markup html tag in sqlplus. 
Here's an example.

SET LINESIZE 4000 
SET VERIFY   OFF 
SET FEEDBACK OFF 
SET PAGESIZE 999 

SET MARKUP HTML ON ENTMAP ON SPOOL ON PREFORMAT OFF 

SPOOL c:\temp\test_xls.xls 

SELECT    object_type     
,         SUBSTR( object_name, 1, 30 ) object     
,         created         
,         last_ddl_time   
,         status         
FROM      user_objects 
ORDER BY  1, 2 
/ 

SPOOL OFF 

SET MARKUP HTML OFF ENTMAP OFF SPOOL OFF PREFORMAT ON 

SET LINESIZE 2000 VERIFY ON FEEDBACK ON 

Kind regards,

Peter Visscher
Amis Services BV 


5 stars   April 1, 2004 - 4pm Central time zone
Reviewer: A reader 
why is your dump_csv a function, and then you call it in a procedure. Why cant it be a procedure 
straight away? Very curious about this.. 


Followup   April 2, 2004 - 9am Central time zone:

you of course can obviously do whatever you want.

I wanted a function that returned the number of rows written.  I then call this function from 
various procedures. 

4 stars Column Headings in csv file   April 8, 2004 - 10am Central time zone
Reviewer: Sri from India
I have gone through the code snippet for adding a column headings... as you have asked to use 
describe_columns... I have tried using it but it is giving error as:
*
ERROR at line 1:
ORA-01007: variable not in select list
ORA-06512: at "SYS.DBMS_SYS_SQL", line 1147
ORA-06512: at "SYS.DBMS_SQL", line 339
ORA-06512: at "CITINEW.DUMP_CSV", line 33
ORA-06512: at "CITINEW.TEST_DUMP_CSV", line 5
ORA-06512: at line 1

I have used:
*l_cursor := dbms_sql.open_cursor;
    dbms_sql.parse( l_cursor, p_query, dbms_sql.native );
    dbms_sql.describe_columns( l_cursor, l_cols, l_desc_tab );
    
    l_status := dbms_sql.execute(l_cursor);

    LOOP
        EXIT WHEN ( dbms_sql.fetch_rows(l_cursor) <= 0 );
        l_separator := '';
        FOR i IN 1 .. l_cols LOOP
            dbms_sql.column_value( l_cursor, i, l_desc_tab(i).col_name );
            utl_file.put( l_output, l_separator || l_desc_tab(i).col_name );
            l_separator := p_separator;
        END LOOP;
        utl_file.new_line( l_output );
        l_cnt := l_cnt+1;
    END LOOP;
    dbms_sql.close_cursor(l_cursor);

    utl_file.fclose( l_output );

please guide wat to code to insert the heading in the csv file.
 


Followup   April 8, 2004 - 10am Central time zone:

umm, what happened to the define column calls after the parse?

take the original code

ADD to it (removing NONE of it) 

5 stars utl_file_dir   April 20, 2004 - 9am Central time zone
Reviewer: Jennifer Chen from Viena, VA USA
Hi Tom,

I have a requirement that will give the user the option to specify the location and name of the 
output file for a query. I checked that I can't modify utl_file_dir on the fly.

SQL> select issys_modifiable from v$parameter where name='utl_file_dir';

ISSYS_MOD
---------
FALSE

What are my alternatives?

Thanks in advance.

 


Followup   April 20, 2004 - 10am Central time zone:

Really?

I choose /usr/oracle/oradata/database/system.dbf as my output destination.  

Oh wait, maybe we don't really want to do that.


Rather -- might you want to list the directories the administrator of the database has deemed 
"these are OK for the oracle process to write into" and present that list to the end user?  Much 
safer...

In 9i, you can use DIRECTORY objects instead of utl_file_dir.  Meaning, you can on the fly "create 
directory FOO as '/tmp'" and use utl_file to write to that directory.  However, that gives you the 
capability to save to system.dbf again.... not so "good" if you let them just write ANYWHERE (cause 
as we know, end users are  going to try to write anywhere if you let them)

Think about this for a little bit first -- you really don't want to write ANYWHERE, really. 

5 stars THANK YOU   April 20, 2004 - 9pm Central time zone
Reviewer: Jennifer Chen from Viena, VA USA
Hi Tom,

I got your point here... 

I have used Oracle9i for almost 2 years and been certified with 9i. I didn't know this New Method 
of Using UTL_FILE in Oracle9i R2 until today. I have tried it, and it worked fine.

Many many thanks... 


5 stars Questions of speed   April 21, 2004 - 2am Central time zone
Reviewer: Alvin from Philippines
I need to extract data from the table and have it in a file too.

Currently i use sqlplus's 'spool'. 

My problem with that approach is because of sqlplus innate tendency to align the columns hence 
storing excessive space. The query finishes quickly but the output to the terminal (or output to 
file) takes much much much longer.

Is the PLSQL program above much faster than spool ? 


Followup   April 21, 2004 - 7pm Central time zone:

http://asktom.oracle.com/~tkyte/flat/index.html

shows my "unloaders".

plsql slowest
sqlplus by itself faster (with set termout off and some formatting)
proc = fastest. 

5 stars Hints in the sql   April 21, 2004 - 4am Central time zone
Reviewer: Alvin from Philippines.
Tried using the dump_csv function and it answered my 'unsuppressed space' problem. Very useful tool 
!

Can i pass a sql with a hint on the dump_csv function ? or will there be any side effects ? 


Followup   April 21, 2004 - 8pm Central time zone:

pass any sql you want. 

4 stars data migration utility   April 21, 2004 - 10am Central time zone
Reviewer: Anurag from INDIA
Hi! tom,

I want to make data migration utility for foxpro & access to oracle in D2K. Can U brief How to 
achieve this.
Pl. do not advise to use migration workbench,  I want to create customize utility for these two 
only.

Pl. Help. 


Followup   April 21, 2004 - 8pm Central time zone:

the only advice i have is "starting writing code"

me, I would set up the heterogenous services and using odbc just "create table IN_ORACLE as select 
* from table_in_access"

 

4 stars Question   April 23, 2004 - 12am Central time zone
Reviewer: Alvin from Philippines
I'm using the dump_csv funtion in a anonymous pl/sql block

declare
l_num number;
begin
l_num:= dump_csv('select /*+ INDEX (RTBETELCALLDETAILRECORDS CDR_MULTI_2) */ * 
                from rtbetelcalldetailrecords
                where groupid=1482 
                and time between  1075593600000   and  1078099199999
                order by time'
, ',' ,'/export/home/oracle/utl_file_dir' ,'tele_200402.csv');

dbms_output.enable(10000);
dbms_output.put_line('-------------------------');
dbms_output.put_line('Rows dumped :'||l_num);
end;
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "SYS.UTL_FILE", line 103
ORA-06512: at "SYS.UTL_FILE", line 120
ORA-06512: at "SYS.UTL_FILE", line 287
ORA-06512: at "RTBETELEPHONY.DUMP_CSV", line 50
ORA-06512: at line 4


Also i ran it as sql only.

  1  select  dump_csv('select /*+ INDEX (RTBETELCALLDETAILRECORDS CDR_MULTI_2) */ *
  2                               from rtbetelcalldetailrecords
  3                              where groupid=1482
  4                               and time between  1075593600000   and  1078099199999
  5                               order by time'
  6  , ',' ,'/export/home/oracle/utl_file_dir' ,'tele_200402.csv')
  7* from dual
11:51:06 rtbetelephony@RTBE.SUNFIRE01> /
select  dump_csv('select /*+ INDEX (RTBETELCALLDETAILRECORDS CDR_MULTI_2) */ *
        *
ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "SYS.UTL_FILE", line 103
ORA-06512: at "SYS.UTL_FILE", line 120
ORA-06512: at "SYS.UTL_FILE", line 287
ORA-06512: at "RTBETELEPHONY.DUMP_CSV", line 50
ORA-06512: at line 1

What i dont get is it runs fine on other partitions and consistently raises these on others despite 
rerunning it. It always raises the error at a specific filesize.
 


Followup   April 23, 2004 - 11am Central time zone:

so, what be line 50 of dump_csv in your database.

it could be you want to:

l_output := utl_file.fopen( p_dir, p_filename, 'w', 32000 );
                                                    ^^^^^^^^^^

default linesize is 1022 bytes, you might be exceeding that. 

4 stars   April 26, 2004 - 8pm Central time zone
Reviewer: A reader 
Tom

When the data which needs to to dumped to csv contains commas, will the CSV format work?

 


Followup   April 27, 2004 - 7am Central time zone:

you would typically "quote" it

"this is, i believe","what you would do",55

to have three fields.... you would just add "quotes" 

4 stars TEXT_IO giving errors   May 5, 2004 - 8am Central time zone
Reviewer: Subramanian from India
Hi 
Good day to you.
I have copied your unloader example with slight modifications.
It fails when the program comes to column_value procedure.
I am using FORMS 5.0 and oracle 8.1.5.
It gives the following error.

ORA-06562: Type of out argument must match type of column or bind
variable.

Please help.
--------
PROCEDURE PRcreate_data IS
   mtablename      varchar2(30);
   column_name_str varchar2(2000);
   select_str      varchar2(2000);
   out_file        TEXT_IO.FILE_TYPE;
   l_cursor_id     integer;
   l_col_count     integer;
   l_column_value  varchar2(4000);
   l_status        number;
   l_separator     varchar2(1);
   l_desc_tbl      dbms_sql.desc_tab;

   cursor table_cur is
   select tablename
   from   upload_tables;

   cursor first_cur is
   select column_name, data_type
   from   user_tab_columns
   where  table_name = mtablename
   order  by column_id;
BEGIN
   out_file := TEXT_IO.FOPEN('c:\echo.txt', 'w');

   TEXT_IO.PUT_LINE(out_file, 'LOAD DATA');
   TEXT_IO.PUT_LINE(out_file, 'INFILE *');

   for table_rec in table_cur
   LOOP
      mtablename := table_rec.tablename;
      TEXT_IO.PUT_LINE(out_file, 'INTO TABLE '||mtablename);
      TEXT_IO.PUT_LINE(out_file, 'FIELDS TERMINATED BY ''|''');

      column_name_str := '(';
      select_str := 'Select ';

      for first_rec in first_cur
      LOOP
         column_name_str := column_name_str||first_rec.column_name||',';
         select_str := select_str||first_rec.column_name||',';
      END LOOP;

      column_name_str := rtrim(column_name_str,',');
      column_name_str := column_name_str||')';

      TEXT_IO.PUT_LINE(out_file, column_name_str);
      TEXT_IO.PUT_LINE(out_file, 'BEGIN DATA');

      select_str := rtrim(select_str,',');
      select_str := select_str||' from '||mtablename;

      l_cursor_id := dbms_sql.open_cursor;
      dbms_sql.parse(l_cursor_id,  select_str, 1);
      dbms_sql.describe_columns( l_cursor_id, l_col_count, l_desc_tbl );

      for i in 1 .. l_col_count
      LOOP
         dbms_sql.define_column( l_cursor_id, i, l_column_value, 4000);
      END LOOP;

      l_status := dbms_sql.execute(l_cursor_id);

      while ( dbms_sql.fetch_rows(l_cursor_id) > 0)
      LOOP
         l_separator := '';
         for i in 1 .. l_col_count
         LOOP
            dbms_sql.column_value( l_cursor_id, i, l_column_value );
            TEXT_IO.PUT_LINE(out_file, l_separator || l_column_value );
            l_separator := '|';
         END LOOP;
      END LOOP;

      dbms_sql.close_cursor(l_cursor_id);
   END LOOP;

   TEXT_IO.fclose( out_file );

EXCEPTION
   WHEN no_data_found THEN
      TEXT_IO.FCLOSE(out_file);

   when others then
      TEXT_IO.FCLOSE(out_file);
      message(sqlerrm);
END;

--------

Thanks in advance
Subramanian Natarajan 


Followup   May 5, 2004 - 9am Central time zone:

in forms, use EXEC_SQL or even better "create_group_from_query" as that will actually array fetch 
to the client. 

2 stars TEXT_IO   May 5, 2004 - 11pm Central time zone
Reviewer: Subrmanian Natarjan from India
Hi Tom
Thank you for your immediate response.
I searched metalink.
It is a bug and is fixed in FORMS 6.0.5.x.
Thanks.
Subramanian Natarajan. 


5 stars Tiny irritating feature!   May 18, 2004 - 9am Central time zone
Reviewer: Phil from UK
Hi Tom
I've searched high and low and annoyingly I am sure it can be done. I have a sql file that prompts 
for a few variables. It then starts a spool and immediately runs another file with a where clause 
using some of the variables. Problem is, I don't want to see it in the spool - is there a way of 
hiding this?

(Top of the export
old  27: FROM dsv.TSKS WHERE JBID = &&jbid ORDER BY ID
new  27: FROM dsv.TSKS WHERE JBID = 123 ORDER BY ID)

And the script is...
SET NEWPAGE 0
SET SPACE 0
SET LINESIZE 400
SET PAGESIZE 0
SET ECHO OFF
SET FEEDBACK OFF
SET HEADING OFF
prompt 'Please enter the name of the filename you wish to produce (include the full path)'
accept filename
prompt 'Which jbid do you wish to export?'
accept jbid
spool &&filename
SET termout off
@ c:\pulljob
spool off
SET termout on
set linesize 80
set feedback on
set heading on
set termout on
set verify on
set trimspool on


(pulljob is basically just a big select statement)
 


Followup   May 18, 2004 - 4pm Central time zone:

set verify off 

5 stars set verify off   May 18, 2004 - 12pm Central time zone
Reviewer: Phil from UK
Use the above to remove the "verification" of substitution variables (sorry to have asked a dumb 
Q!) 


5 stars how i pass parameter to (dump_csv) function   May 31, 2004 - 10am Central time zone
Reviewer: A reader from usa


5 stars All the values are Right Justified!!!   June 14, 2004 - 9pm Central time zone
Reviewer: Arindam from US
Tom,
   I have tried to run this procedure and I have been sucessful, thanks much. But I have a problem. 
All the column values comes as right justified. Whether its Varchar2 or Number. I would like to 
have Varchar's as Left justified and Number's as right. I am using a view to create the file. The 
view has correct datatype, I mean Varchar's and Numbers. Any insight on this ? May be am I am 
missing something. Please help.  


Followup   June 15, 2004 - 3pm Central time zone:

in the view, use to_char(num,'999999999.99') instead of just num

my code doesn't even justify anything? 

5 stars   July 5, 2004 - 10pm Central time zone
Reviewer: A reader 
Tom

I write a lot of queries for my users. Now I want to use the dump_csv fundtion and set all those 
queries up using a front end GUI( Forms 9i).

Please give me your opinion and solution. I want to store all these queries in the database, and 
pull them up based on the query selected using the forms gui interface.

Now the issue I'm encountering is , each of these queries also take parameters to filter data. I 
want your advise as to how I should set up these queries for parameters. Once the query is selected 
from the gui interface and parameters are supplied , then the query as a whole can be sent as a 
paramter to dump_csv routine. But how should we dynamically decide which parameters to supply based 
on the query selected.

For example I might select query 1 which is 

select empno, ename from emp where deptno = 

Next I might select query 2 which can be 

select ename, dname
from dept d, emp e
where d.deptno = e.deptno
and ename =


Is there any way we can modify the dump_csv parameter and make this happen.

I repeat the main issue is with supplying parameters, to the query selected.

Thanks a lot in advance 


Followup   July 6, 2004 - 7am Central time zone:

either use cursor_sharing=force, or use an application context.

You'll want to save in your schema the QUERY and the names of the 'binds' you want to supply -- so 
you can build a UI to gather this information (eg: dont save just a query, but a query plus other 
metadata)

ops$tkyte@ORA9IR2> create or replace package my_pkg
  2      AUTHID CURRENT_USER
  3  as
  4          function  dump_csv( p_query     in varchar2,
  5                          p_separator in varchar2 default ',',
  6                          p_dir       in varchar2 ,
  7                          p_filename  in varchar2 )
  8      return number;
  9
 10          procedure bind( p_name in varchar2, p_val in varchar2 );
 11  end;
 12  /
 
Package created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create or replace context my_ctx using my_pkg
  2  /
 
Context created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create or replace package body my_pkg
  2  as
  3
  4  procedure bind( p_name in varchar2, p_val in varchar2 )
  5  is
  6  begin
  7          dbms_session.set_context( 'my_ctx', p_name, p_val );
  8  end;
  9
 10
 11
 12  function  dump_csv( p_query        in varchar2,
 13                      p_separator in varchar2 default ',',
 14                      p_dir       in varchar2 ,
 15                      p_filename  in varchar2 )

.......


ops$tkyte@ORA9IR2> exec my_pkg.bind( 'ename', '%A%' );
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> variable n number
ops$tkyte@ORA9IR2> exec :n := my_pkg.dump_csv( 'select * from scott.emp where ename like 
sys_context(''my_ctx'',''ename'')', ',', '/tmp', 'test.dat' );
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> print n
 
         N
----------
         7
 
 

4 stars Each query can have diff number of parameters   July 6, 2004 - 11am Central time zone
Reviewer: A reader 
Each query can have different number of parameters, how do we set the session context for each of 
them. What do mean by store the metadata about the query. What all do you think we should store in 
the schema.

Thanks 


Followup   July 6, 2004 - 11am Central time zone:

fine, you just call bind over and over for each parameter?  not a problem there.

I think you should store a master row with the query and a series of detail rows that have the 
query_id as a foreign key and the NAME of the bind variable.

eg:

select * from t where x = sys_context('my_ctx','x') and y > sys_context('my_ctx','y')

should have two rows stored in a detail table telling you "x and y are the inputs" -- so you don't 
have to parse that information out. 

3 stars Well, lets see   July 6, 2004 - 6pm Central time zone
Reviewer: A reader 
The users selects a query
The gui interface pull up the associated parameter into and displays items on the interface, so 
that the user can enter parameters.

Once the user enters the parameters, then we need to  just call bind over and over for each 
parameter.

Is that the way you are seeing it ? 


Followup   July 6, 2004 - 7pm Central time zone:

yes.
3 stars Interesting Proposition   July 6, 2004 - 7pm Central time zone
Reviewer: A reader 
Tom

I have another interesting proposition here, apart from writing to a csv file, if I also want to 
display the data returned by the query in the form, what is the best way to do it.
This is challenging actually because, each query has different number of columns. Can we use the 
ref cursor to achieve this, can I have a ref cursor return a fixed set of 30 values out, 
irrespective of the number of columns a query has. Yes, obviously if the query has more than 50 
columns the query will fail, but that is o.k.

If this is possible then we can build a fixed width block of 30  columns in oracle form and 
pupulate it using a stored procedure each time. 


Followup   July 6, 2004 - 7pm Central time zone:

sure, you can always query:


select ename, empno, job, null, null, null, ....., null
  from emp


and dump that to a file and have a default block built on it (heck, you can even set the item 
property to hidden for the 27 extra columns and unhide them later) 

3 stars Error   July 6, 2004 - 11pm Central time zone
Reviewer: A reader 
I. Now we are creating a table storing the query in it

create table t5 ( q varchar2(4000));

insert into t5 values ( 'select ename, empno, job, null, null, null,  null from emp') ;

commit;



II. Before calling a query from the table, lets try running it  statically and not calling it from 
the database

SQL> create or replace package types 
  2  as 
  3      type cursorType is ref cursor; 
  4  end; 
  5  / 

Package created.

SQL> 
SQL> create or replace procedure getemps( p_cursor in out types.cursorType )
  2  
  3  as
  4  
  5  begin
  6  
  7        open p_cursor for select ename, empno, job, null, null, null,  null from emp;
  8  
  9  end;
 10  /

Procedure created.

SQL> variable c refcursor 
SQL> exec :c := sp_ListEmp 

PL/SQL procedure successfully completed.

SQL> print c

ENAME           EMPNO
---------- ----------
ADAMS            7876
ALLEN            7499
BLAKE            7698
CLARK            7782
FORD             7902
JAMES            7900
JONES            7566
KING             7839
MARTIN           7654
MILLER           7934
SCOTT            7788

ENAME           EMPNO
---------- ----------
SMITH            7369
TURNER           7844
WARD             7521

14 rows selected.


Tom, can you give some hint as to how this package should be changed in order to accept queries 
from the database.

I ran into the below error..




SQL> ed
Wrote file afiedt.buf

  1  create or replace procedure getemps( p_cursor in out types.cursorType )
  2  as
  3  lv_query varchar2(4000);
  4  begin
  5  select q into lv_query from t5;
  6        open p_cursor for lv_query;
  7* end;
  8  /

Procedure created.

SQL> variable c refcursor 
SQL> exec :c := sp_ListEmp 

PL/SQL procedure successfully completed.

SQL> print c

ENAME           EMPNO
---------- ----------
ADAMS            7876
ALLEN            7499
BLAKE            7698
CLARK            7782
FORD             7902
JAMES            7900
JONES            7566
KING             7839
MARTIN           7654
MILLER           7934
SCOTT            7788

ENAME           EMPNO
---------- ----------
SMITH            7369
TURNER           7844
WARD             7521

14 rows selected.

SQL> 
SQL> exec getEmps( :c )
BEGIN getEmps( :c ); END;

*
ERROR at line 1:
ORA-00936: missing expression
ORA-06512: at "SCOTT.GETEMPS", line 6
ORA-06512: at line 1


SQL> 
SQL> print c
ERROR:
ORA-24338: statement handle not executed





 


Followup   July 7, 2004 - 7am Central time zone:

ops$tkyte@ORA9IR2> create table t5 ( q varchar2(4000));
 
Table created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> insert into t5 values
  2  ( 'select ename, empno, job, null, null, null,     null from emp') ;
 
1 row created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> commit;
 
Commit complete.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> variable x refcursor
ops$tkyte@ORA9IR2> declare
  2          l_stmt varchar2(4000);
  3  begin
  4          select q into l_stmt from t5;
  5          open :x for l_stmt;
  6  end;
  7  /
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> print x
 
ENAME           EMPNO JOB       N N N N
---------- ---------- --------- - - - -
SMITH            7369 CLERK
ALLEN            7499 SALESMAN
WARD             7521 SALESMAN
JONES            7566 MANAGER
MARTIN           7654 SALESMAN
BLAKE            7698 MANAGER
CLARK            7782 MANAGER
SCOTT            7788 ANALYST
KING             7839 PRESIDENT
TURNER           7844 SALESMAN
ADAMS            7876 CLERK
JAMES            7900 CLERK
FORD             7902 ANALYST
MILLER           7934 CLERK
 
14 rows selected.


works for me, check your stored query, you did not cut and paste a sqlplus session, so I can only 
guess you did something different on the insert -- i cannot see the actual query you fetched out
 

3 stars   July 6, 2004 - 11pm Central time zone
Reviewer: A reader 
Tom

Can you show me how the query should be stored in the database with sys_context applied to the 
parameter column for a query.

And then when the query is called, how should we use the bind procedure above and pass values to 
multiple parameters.

Thanks in advance 


Followup   July 7, 2004 - 7am Central time zone:

I already did.


look above.  replace my character string constant with your plsql variable.
call "bind" over and over.

done. 

5 stars What about Blob?   September 6, 2004 - 8am Central time zone
Reviewer: Zeenat from Singapore
Thanks for the solution Tom. However how do we handle columns of blob data types 
in such cases? 

Appreciate your help,
Zeenat 

 


Followup   September 6, 2004 - 10am Central time zone:

dbms_sql can handle blobs.

YOU have to decide what you want to do with one upon detecting it. 

5 stars   September 6, 2004 - 11am Central time zone
Reviewer: A reader from France


3 stars can we put BLOB data to flat file,is this advisable   September 7, 2004 - 3am Central time zone
Reviewer: Sachi from Bangalore,India
I tried this function with table having BLOB data,but it gave inconsistent data type error.

if dbms_sql can handle blob data how do we change the above function to put the blob data to flat 
file and is this advisable? 


Followup   September 7, 2004 - 7am Central time zone:

depends on your release, current versions of the software have a "put raw" api in utl_file.


dbms_sql can get a blob, dbms_lob can read/write the blob, utl_file in current releases can be used 
to write binary data. 

4 stars unload / load Oracle tables with clob, blob , etc into flat file using WisdomForce Fastreader   January 8, 2005 - 9pm Central time zone
Reviewer: Joseph from S.F. CA USA
DOn't know if this related to the subject of question. But it defintely answers some comments here.

There is an effective and very fast tool FastReader www.wisdomforce.com which unload Oracle tables 
with clob, blob ,etc into flat file and automatically generates control files and scripts for 
loading data.  


5 stars Follow-up on ref cursor usage   February 8, 2005 - 9pm Central time zone
Reviewer: Paul from Singapore
Hi Tom,
One of the follow-ups was asking exactly the situation I am banging my head with right now => how 
to dump the results given an arbitrary ref cursor as the starting input.
Your answer dampened my hopes => you cannot -- a ref cursor is not "procedurally processable" by 
PLSQL.
But I still have some hope! Is this the final word on the matter? Patently, dbms_sql itself is able 
to track and manage its "cursors" but I'm not sure what internal tricks it is resorting to.
So the question is: assuming that the input is a ref cursor, and that this is an immutable given, 
is there _any_ way of at least describing the columns, or getting a column count? 
Any words of wisdom most appreciated! 


Followup   February 9, 2005 - 2am Central time zone:

my final answer:

you cannot -- a ref cursor is not "procedurally 
processable" by PLSQL.

in a 3gl that lets you "describe" (like java -- meaning you could write a java STORED procedure 
that does this), you can.

(suppose in plsql you could get the column count and describe them, what than?  you still could not 
"fetch" them -- for fetch is processed at COMPILE time in plsql) 

5 stars To Paul   February 9, 2005 - 1pm Central time zone
Reviewer: Menon 
Like Tom says, you can do it in Java.
See 
http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:30987881259352#31044380940455
and the related thread for an example that you can probably
build on.
 


4 stars Ref Cursor Metadata in PL/SQL...not possible?   March 30, 2005 - 1pm Central time zone
Reviewer: Bipin from CA USA
Tom,

I have same question as Paul has.  I read your book effective oracle by design,, but it seems i 
need to read it again in order to understnad ref cursors.  But in any case, it's very difficult for 
me to digest this fact. 

It's kinda difficult to believe that you can read RefCursor metadata and navigate columns and rows 
of the result set in Java but not in PL/SQL.  Isn't PL/SQL a procedural language?  Isn't ref cursor 
is a cursor after all?  

As Paul mentioned in above posting, it is a very common requirement to write such code.  Where does 
Java get this information from? It has to be part of 'opened RefCursor', no?  If yes, why can't 
Oracle come up with some package like dbms_sql that gets us this information?

Is there any other reading you can suggest?

Thanks again for supporting oracle develoepr community.

-Bipin 


Followup   March 30, 2005 - 2pm Central time zone:

in PLSQL, if you want to do dynamic sql and navigate the result set column by column (because the 
number, types and names change) you use DBMS_SQL.

If you know at compile time, we can use a ref cursor.

PLSQL just doesn't have the language constructs (java uses an API) to do this -- java doesn't 
either.

In PLSQL if you want to do it like java, you do it just like java, dbms_sql is alot like ResultSet. 

3 stars Enhancement request for Oracle?   March 30, 2005 - 2pm Central time zone
Reviewer: Bipin from CA USA
Thanks for your quick answer Tom.

Do you think it is a case of a good enhancement request for Oracle or it's just impossible even 
with some new oracle supplied PL/SQL API?

I think this is the last question I am going to ask on this subject.

Thanks for your patience.

One off the tangent but related question:
If I write this as Java SP, can I still debug it in Jdeveloper? (I can of course, find it out 
myself but just being greedy)

By the way, I started using Jdeveloper after reading your book and this is the first time, it's so 
easy to debug a PL/SQL code! Thankyou!

Have a good day.

-Bipin. 


Followup   March 30, 2005 - 3pm Central time zone:

but dbms_sql does it all, it is the very api you are looking for? 

3 stars Clarification on the question   March 30, 2005 - 4pm Central time zone
Reviewer: Bipin from CA USA
I previously thought I won't followup on this but it seems that I have not made my question clear 
enough. Le me try:

I am aware about dbms_sql but dbms_sql does NOT take ref cursor input.  It expects a cursor? (an 
integer returned by dbms_sql.parse?)

The situation I have is:

We have several SPs that are taking XML string as input and returning ref cursor for client use. 
This ref cursor structure of course will be different for different SPs. 

I want to write a gneralized SP that will take SP name and XML string as inputs and test/debug such 
a SP and print ref cursor output.  In order for me to do this, I need to get access to ref cursor 
metadata as everything (column names, types, # of columns etc.) could be different from one SP to 
another SP.

Since the original SP returns a ref cursor and not an integer, I can NOT use dbms_sql?  So, 
question is does/should PL/SQL have an equivalent of dbms_sql that takes ref cursor as input?

I can change the original SP to use dbms_sql rather than ref cursor but then I change the interface 
for the clients who expect a ref cursor as output.  So, that's not an option for me.

Hope this explains the questions.

Thanks again for your help.

-Bipin. 


Followup   March 30, 2005 - 5pm Central time zone:

correct.  my point is - you want java api like access to a result set in plsql, you will use 
dbms_sql.

If you want the declaritive way, where the language and SQL are tightly bound, you'll use ref 
cursors.


You'll have to write that test routine in C, Java, or some other 3gl as PLSQL doesn't do that. 

4 stars ThankYou.   March 30, 2005 - 5pm Central time zone
Reviewer: Bipin from CA USA
I think I will try Java SP. 
Thanks for your time, Tom. 


4 stars backup and recovery   April 6, 2005 - 9am Central time zone
Reviewer: Parag J Patankar from India
Hi Tom,

We are in Oracle9R2 and we would like to preserver our data atleast for 7 years as per the 
instructions from various authorities. For this reason we have decided to preserve data in text 
format. Because we are not sure that if we have taken backup in oracle7 it will work in oracle10g 
or  higher versions ( at the time of requirement suppose at end of 7th year we require this data 
...) and again if it is not working we have to restore OS also ..etc and it will big massive work. 
Such restoration will 
make our life difficult. 

     pl tell me something wrong in this above thinking process.

So we have decided to 
1. Dump all data in text files using separator character ( got an answer in this 
question )
2. all tables and indexes definitions. Kindly guide me which one is best for 
this step ( shall we do by exp and imp with indexfile option ? or extract 
definitions from dbms_metadata ...etc )
3. Load this text data into the tables . 

   Can you help me 
a. which is the best method to load data into these tables ? (using sqlloader or text file ...etc )
b. by amending your script to provide loading script ?

regards & thanks
pjp 


Followup   April 6, 2005 - 1pm Central time zone:

... preserve data in text format ...

why? databases where invented to....
store data.


Just upgrade as you upgrade?   

4 stars "Preserving data in text format"   April 7, 2005 - 10am Central time zone
Reviewer: Parag Jayant Patankar from India
Hi Tom,

Regarding your answer to my question in this thread, sorry I have not mentioned that, we are in 
banking industy and every day we are generating huge number of transactions every day". Due to 
various reasons our vendor is purging transactions from our production database on a monthly basis. 
This database is outside our country so we are having little control over it, secondly I can not 
take decision on this.

In this scenario we want to preserve our data in text format. Can you help me regarding my 
questions ?

I also observed that downloading of data of 45000 records by using your utility into flat file took 
around 9 mins. Can you tell me is it normal ?

regards & thanks
pjp 


Followup   April 7, 2005 - 11am Central time zone:

which utility.

I have 3

o plsql, slow but easy, flexible
o sqlplus, faster, less flexible
o pro*c -- much faster.

45,000 records in 9 minutes seems very excessive.

Ok, silly little desktop pc.  table T is a copy of ALL_OBJECTS, using sqlplus:


[tkyte@localhost tkyte]$ time flat / t > t.dat
 
real    0m4.228s
user    0m3.490s
sys     0m0.310s
[tkyte@localhost tkyte]$ wc t.dat
  27809   38840 2732614 t.dat
[tkyte@localhost tkyte]$


so, less than 5 seconds.



Using pro*c

[tkyte@localhost array_flat]$ time array_flat userid=/ 'sqlstmt=select * from t' arraysize=100 > 
t.dat
 
Connected to ORACLE as user: /
 
Unloading 'select * from t'
Array size = 100
OWNER,OBJECT_NAME,SUBOBJECT_NAME,OBJECT_ID,DATA_OBJECT_ID,OBJECT_TYPE,CREATED,LAST_DDL_TIME,TIMESTAM
P,STATUS,TEMPORARY,GENERATED,SECONDARY
27809 rows extracted
 
real    0m0.694s
user    0m0.440s
sys     0m0.020s
[tkyte@localhost array_flat]$ wc t.dat
  27809   94458 3344412 t.dat
[tkyte@localhost array_flat]$

Less than a second.  9 minutes would be considered excessive. 

4 stars   April 7, 2005 - 12pm Central time zone
Reviewer: Parag Jayant Patankar from India
Hi Tom,

I have used pl/sql procedure to download data

create or replace function  dump_csv( p_query     in varchar2,
                                      p_separator in varchar2 
                                                    default ',',
                                      p_dir       in varchar2 ,
                                      p_filename  in varchar2 ) ...etc. I will recheck again and 
inform you.

you have mentioned that pl/sql is slower but flexible to use, sqlplus is faster ..

So if I want to execute select statement in sql or pl/sql which one is faster ? ( my understanding 
from your valuable help is PL/SQL is faster because it parse the query and execute again and again 
so giving us good parse/execute ratio while SQL is not doing so it always at least doing soft 
parse)

rgards & thanks
pjp 


Followup   April 7, 2005 - 12pm Central time zone:

but in your case you have only one query.

sqlplus is faster because it is array fetching,  my simple plsql one is not array fetching (you 
could correct that)

sqlplus is faster because it is C code writing data, plsql is not .


You could make the plsql go faster simply by building a "line" and then utl_file.putting it, rather 
than column by column.

You could make the plsql go faster by array fetching.


You could make the plsql go faster by not using dbms_sql (since you know your query, you can use 
static sql, fetch BULK COLLECT, build a 32k buffer of data, call utl_file a few times instead of 
number of rows * number of columns....)


but I doubt you would beat the pro*c program. 

5 stars more than 4000 char query   April 7, 2005 - 3pm Central time zone
Reviewer: A reader 
Tom,
Was wondering if you can provide any suggestions if the query(variable p_query in dump_csv) is more 
than 4000 charac?

Thank you
 


Followup   April 7, 2005 - 4pm Central time zone:

32k is the limit for a plsql varchar2

after that, send an array, use the dbms_sql.parse that takes an array that can have a query of any 
size (documented in the supplied packages guide) 

5 stars Cannot directly access remote package variable?   April 21, 2005 - 10pm Central time zone
Reviewer: A reader 
Tom,
In my form I have this code:

pa_project_pub.load_key_member(p_return_status     => :key_member_projects.replacement_km_status,

                               p_person_id         => :key_member_projects.replacement_person_id,

                               p_project_role_type => :key_member_projects.op_role,

                               p_start_date        => :key_member_projects.end_date_active);

 

When compiled it produces this error

 
Implementation Restriction: ‘PA_INTERFACE_UTILS_PUB.G_PA_MISS_NUM’ Cannot directly access remote 
package variable or cursor

 

So, I tried the following in sql and got no error.

 

declare

  return_status varchar2(2000);

begin

      pa_project_pub.load_key_member(p_return_status     => return_status,

                                     p_person_id         => 10,

                                     p_project_role_type => 'OP Role',

                                     p_start_date        => sysdate);

end;

 


Followup   April 22, 2005 - 10am Central time zone:

there must be other inputs that are defaulted to that procedure and are defaulted to package 
variables.

describe the procedure in sqlplus, find and supply ALL inputs that are defaulted to package 
variables. 

4 stars   April 22, 2005 - 1pm Central time zone
Reviewer: A reader 
>desc pa_project_pub
PROCEDURE LOAD_KEY_MEMBER
 Argument Name                  Type                    In/Out Default?
 ------------------------------ ----------------------- ------ --------
 P_API_VERSION_NUMBER           NUMBER                  IN     DEFAULT
 P_INIT_MSG_LIST                VARCHAR2                IN     DEFAULT
 P_RETURN_STATUS                VARCHAR2                OUT
 P_PERSON_ID                    NUMBER                  IN     DEFAULT
 P_PROJECT_ROLE_TYPE            VARCHAR2                IN     DEFAULT
 P_START_DATE                   DATE                    IN     DEFAULT
 P_END_DATE                     DATE                    IN     DEFAULT 


Followup   April 22, 2005 - 1pm Central time zone:

yes, like I said -- fill in the defaults -- one of them is defaulted to a package variable and 
forms cannot use that default.

you can also select text from all_source where name = 'THAT_PKG' and type = 'PACKAGE' order by 
line; to see the actual spec and what all things are defaulted to. 

5 stars Using store procedure how can i export table into flat file   April 28, 2005 - 6am Central time zone
Reviewer: Surya Nagendra Varma from India
Using pl/sql store procedure how can i export tables into flat file and the flat file should be 
named as table name with date 


Followup   April 28, 2005 - 8am Central time zone:

http://asktom.oracle.com/~tkyte/flat/index.html

5 stars How to use on Unix   June 1, 2005 - 10am Central time zone
Reviewer: Rash from toronto, on canada
Hi Tom,

I have to extract oracle table data to text file and for that I want to use this proc. Its a 
scheduled job which is running on Unix server. Can you please tell me what changes do I need to 
make in INIT.ORA and how am I suppose to use utl_file ? What other modifications do i need to make 
it running on Unix. I think these are very basic questions, but I am new to this environment and 
have to meet very close deadline.

Thanks in advance.

Rash

 


Followup   June 1, 2005 - 11am Central time zone:

database version? 

5 stars How to use on Unix   June 1, 2005 - 11am Central time zone
Reviewer: Rash from toronto, canada
Hi,

oracle version is 9.2.0.6

Thanks for very very quick reply

Rash 


Followup   June 1, 2005 - 12pm Central time zone:

use a directory object

create or replace directory myfiles as '/tmp'
/


and utl_file can write to that, no setup needed.  instead of using an OS path in utl_file, just use 
'MYFILES' (in UPPERCASE) 

4 stars dbms_job + psp = html file?   August 2, 2005 - 1am Central time zone
Reviewer: ht from california
Hi Tom,
I've generated html output with SQL*Plus's "set markup html on" feature.  

Is it possible to use dbms_job to generate a PSP into a pre-defined utl_dir location?  

TIA
ht 


Followup   August 2, 2005 - 7am Central time zone:

http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:347617533333

4 stars   August 10, 2005 - 2pm Central time zone
Reviewer: Dawar Naqvi from Los Angeles, California, USA
Tom,

You have answered just 2nd above to me as:

create or replace directory myfiles as '/tmp'
/


and utl_file can write to that, no setup needed.  instead of using an OS path in 
utl_file, just use 'MYFILES' (in UPPERCASE) 

Now my database version is 10.1.0.3.0.
Does it apply on my database also?

Regards,
Dawar



 
 


Followup   August 11, 2005 - 8am Central time zone:

yes.
4 stars create CSV file from Oracle table   August 10, 2005 - 2pm Central time zone
Reviewer: Dawar Naqvi from LA, CA, USA
Tom,

DB version is 10.1.0.3.0.

I would like to create CSV file from Oracle table.

I did not find any test.dat file under /tmp.


SQL> create or replace function  dump_csv( p_query     in varchar2,
  2                                        p_separator in varchar2 
  3                                                      default ',',
  4                                        p_dir       in varchar2 ,
  5                                        p_filename  in varchar2 )
  6  return number
  7  AUTHID CURRENT_USER
  8  is
  9      l_output        utl_file.file_type;
 10      l_theCursor     integer default dbms_sql.open_cursor;
 11      l_columnValue   varchar2(2000);
 12      l_status        integer;
 13      l_colCnt        number default 0;
 14      l_separator     varchar2(10) default '';
 15      l_cnt           number default 0;
 16  begin
 17      l_output := utl_file.fopen( p_dir, p_filename, 'w' );
 18  
 19      dbms_sql.parse(  l_theCursor,  p_query, dbms_sql.native );
 20  
 21      for i in 1 .. 255 loop
 22          begin
 23              dbms_sql.define_column( l_theCursor, i, 
 24                                      l_columnValue, 2000 );
 25              l_colCnt := i;
 26          exception
 27              when others then
 28                  if ( sqlcode = -1007 ) then exit;
 29                  else
 30                      raise;
 31                  end if;
 32          end;
 33      end loop;
 34  
 35      dbms_sql.define_column( l_theCursor, 1, l_columnValue, 
 36                              2000 );
 37  
 38      l_status := dbms_sql.execute(l_theCursor);
 39  
 40      loop
 41          exit when ( dbms_sql.fetch_rows(l_theCursor) <= 0 );
 42          l_separator := '';
 43          for i in 1 .. l_colCnt loop
 44              dbms_sql.column_value( l_theCursor, i, 
 45                                     l_columnValue );
 46              utl_file.put( l_output, l_separator ||  
 47                                      l_columnValue );
 48              l_separator := p_separator;
 49          end loop;
 50          utl_file.new_line( l_output );
 51          l_cnt := l_cnt+1;
 52      end loop;
 53      dbms_sql.close_cursor(l_theCursor);
 54  
 55      utl_file.fclose( l_output );
 56      return l_cnt;
 57  end dump_csv;
 58  /

Function created.

SQL> create or replace procedure test_dump_csv
  2  as
  3      l_rows  number;
  4  begin
  5      l_rows := dump_csv( 'select * 
  6                             from all_users 
  7                             where rownum < 25', 
  8                          ',', '/tmp', 'test.dat' );
  9  end;
 10  /

Procedure created.

SQL> create or replace directory myfiles as '/tmp';

Directory created.

SQL> create or replace procedure test_dump_csv
  2  as
  3      l_rows  number;
  4  begin
  5      l_rows := dump_csv( 'select * 
  6                             from all_users 
  7                             where rownum < 25', 
  8   ',','MYFILES','test.dat' );
  9  end;
 10  /

Procedure created.

Am I missing any thing?

Regards,
Dawar 


Followup   August 11, 2005 - 8am Central time zone:

well, you didn't seem to actually RUN IT.  

You created procedures, did you run them? 

5 stars create CSV file from Oracle table   August 15, 2005 - 1pm Central time zone
Reviewer: Dawar Naqvi from Los Angeles, California, USA
Yes.

I got it, thanks.

exec dbms_output.put_line( dump_csv( 'select * from employee_items where rownum < 5', ',', 
'MYFILES', 'test.csv' ) )  


5 stars working fine but dont understand   August 19, 2005 - 9am Central time zone
Reviewer: A reader 
Tom,

Thanks for making this code available.
But I dont understand this part of the code. Could you plz explain?

 FOR i IN 1 .. 255 LOOP
        BEGIN
            DBMS_SQL.DEFINE_COLUMN( l_theCursor, i,  
                                    l_columnValue, 2000 );
            l_colCnt := i;
        EXCEPTION
            WHEN OTHERS THEN
                IF ( SQLCODE = -1007 ) THEN EXIT;
                ELSE
                    RAISE;
                END IF;
        END;
    END LOOP;

What are these values 255 & 2000 for?
What purpose do they solve?

Thanks as always. 


Followup   August 20, 2005 - 4pm Central time zone:

This is old code that pre-dates the describe API in dbms_sql.

I'll define up to the first 255 columns (ignoring the rest... used to be the limit to the number of 
columns) and stop defining the first time Oracle raises 

[tkyte@desktop tkyte]$ oerr ora 1007
01007, 00000, "variable not in select list"
// *Cause:
// *Action:


telling me I've gone past the last column you selected. 

3 stars naging session while spooling off   August 22, 2005 - 7pm Central time zone
Reviewer: Denis from Los Angeles, CA
Hi

I did some testing to compare performance between 'spool off' solution and this PL/SQL procedure. 
Output file was not really big just about 100MB. Total timing was almost the same for both 
solutions, but CPU for PL/SQL was much higher. 

"Spool off" has interesting behaviour it actually loads file pretty fast and after this it just 
doing something else for some time, and then it releases the session. If sql loader would release 
session right after file is created and command would be completed, then timing for sqlloader would 
be very short.


Now I am doing spool off to generate big files about 2GB each. It creates file but starts writing 
only in some time (30 minutes for example). Looks like it just loads as much as it can to memory 
first. After file is completed session is not released, actual command is hanging and I can't 
rename file, antil I close console manually.  Can you explain this behaviour? what should be done 
here?

I have 10g SE on Windows.

here is my command and sql file:

E:\masterfeed.cmd scott/tiger E:\masterfeed.sql E:\feeds\feed_0.sql AlbumTrackRating_0 
2005-08-22_00:00:00

------------------------------------------------------

@echo off

if "%1"=="" goto :usage
if "%2"=="" goto :usage
if "%3"=="" goto :usage
if "%4"=="" goto :usage
if "%5"=="" goto :usage


sqlplus -s %1 @%2 %3 %4 %5 

goto :done

:Usage

echo "usage         feed un/pw [script] [filename] [table_name] [date in format 
2005-07-28_00:00:00]"

:done


-------------------------------------

set    wrap off
set linesize 100
set    feedback off
set    pagesize 0
set    verify off
set termout off

spool &1

select  userID ||chr(9)|| albumTrackID ||chr(9)|| rating
from    &2 a1
where exists 
(select userID from &2 a2 
where a2.userID=a1.userID
and dateModified between to_date('&4','YYYY-MM-DD_HH24:MI:SS')-30 and 
to_date('&4','YYYY-MM-DD_HH24:MI:SS')
and rating<>255
)
Order by userID asc
/

spool off
set termout on
@&1
exit

 


3 stars hanging command while spooling off   August 22, 2005 - 7pm Central time zone
Reviewer: Denis from Los Angeles, CA
Sorry, I made a type in my previous posting. 


3 stars D2K(Clear Block)   August 23, 2005 - 1am Central time zone
Reviewer: Prasanna from India
Hi Tom, 
     Im not much into D2K, I have a problem like this. A form contains 2 blocks. There are 2 radio 
buttons on a form, when I change this options ( Radio ) then the first block contents need to be 
cleard. I tried giving Clear_Block('my') but it is disabled all the contents on the form, when I 
tried giving Clear_List it cleared 2 blocks contents. 
I hope Iam clear with my question. I tried even giving No_Validate, but it of no use. 

Waiting for your reply.

Thanks & Regards,
Prasanna
 


Followup   August 24, 2005 - 3am Central time zone:

please peek at otn.oracle.com -> discussion forums.  It has been over a decade since I last touched 
forms, they have a forum for forms over there. 

3 stars comparing pl/sql to spool off   August 24, 2005 - 11am Central time zone
Reviewer: Denis from Los Angeles, CA
Tom, could you please, answer the questions above about spool off behavior?

Thanks a lot
Denis 


Followup   August 24, 2005 - 6pm Central time zone:

I'm not sure what you mean by the spool off behaviour, you said


...It creates file 
but starts writing only in some time (30 minutes for example)
...

but that is not "spool off behaviour", that is whatever was happening BEFORE the spool off 

3 stars spool off   August 24, 2005 - 8pm Central time zone
Reviewer: Denis from Los Angeles, CA
Tom,

as you can see I am using this solution 
http://asktom.oracle.com/~tkyte/flat/index.html
There are 2 situations. 
1. small file - 100Mb. Data is written to file very fast. Command is not completed and session is 
hanging for some time after data was written to file last time. Then, after some time command is 
completed and session release. What sqlplus is doing after it completed writing to file? 

2. Big file 2GB. File is created right away, but data  started writing to file in 30 minutes. What 
is happening at this time? 
Command is not completed and session is hanging after data was written to file last time. So it 
looks like it completed writing to file, but is doing something else? what is this something else? 
I was wating for a day to see if command would be completed by itself. So I had manually to close 
console window. Similar situation to case 1, the difference is that comman is not completed by 
itself, or it need more time then I waited.

In both cases file had all data I need - all necessery data was written to file.
This is actual query:
==============
select  a.userID ||chr(9)|| a.AlbumTrackID ||chr(9)|| a.rating
from    userAlbumTrackRating a
where exists 
(select userID from userAlbumTrackRating b 
where b.userID=a.userID
and b.dateModified between to_date('1-AUG-05') and to_date('31-AUG-05')
)
Order by a.userID asc
====================================

Please, advice.

Denis  

Thanks
Denis 


Followup   August 25, 2005 - 3am Central time zone:

1) nothing, like I said, it is still doing the query.

Perhaps you have a big table, but the table has very very few rows (relative to its overall size).  
So, it takes a long time to full scan - reading all of the empty blocks.

Trace it and see.

2) could be the opposite, the query you are running takes a long time to get the first row and then 
they start coming.

 

3 stars what about bulk fetching   August 30, 2005 - 12pm Central time zone
Reviewer: Pet from USA
If we've version 9i release 2, can we change these extract to implment bulk fetch with limit 
clause? Is that possbile? Please let me know.  


Followup   August 30, 2005 - 12pm Central time zone:

you don't even need 9i -- dbms_sql has an array interface, you can use it, I just didn't do it for 
this demo.

if you have expert one on one Oracle -- I cover the array fetching with dbms_sql. 

3 stars dynamic   August 30, 2005 - 4pm Central time zone
Reviewer: A reader 
I looked at DBMS_SQL.define_array , it seems that I need to declare set of local array variable to 
able to bind into., But in my case I don't know the SQL Query will be passed as parameter.

Basically I need to create a flat file if the query is passed to pl/sql procedure as one parameter, 
followed by bind variables inside the query as rest of the parameters.
I need to do bulk collect and array fetching.

Please let me know your thoughts    


Followup   August 31, 2005 - 1am Central time zone:

You would array fetch column 1 into array slots 1..100
                      column 2 into array slots 101..200
                      column 3 into array slots 201..300

and so on, you only need one array. 

4 stars Error when puuting the function inside a cursor   September 21, 2005 - 5am Central time zone
Reviewer: Thilak from India
Hi TOm,

The code was very useful...

But i have only one doubt...

My problem is i am calling the dump_csv inside a cursor, the cursor actually fetches the query  
,separator,dir,filename from a table. I have some five records from the table, the file is created 
for the first record, but the second record it throws the following exception

ORA-29283: invalid file operation
ORA-06512: at "SYS.UTL_FILE", line 475
ORA-29283: invalid file operation
ORA-06512: at "WHTDEV.DUMP_CSV", line 17
ORA-06512: at "WHTDEV.SP_RRS_ODS", line 97
ORA-06512: at line 1

All the file names are unique.. i couldnt figure the problem
 


Followup   September 21, 2005 - 7pm Central time zone:

worked for me, 

ops$tkyte@ORA9IR2> create or replace directory tmp as '/tmp';

Directory created.

ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> declare
  2          n number;
  3  begin
  4          n:=dump_csv( 'select 1 x, sysdate y, dummy from dual',  ',', 'TMP', 't1.dat' );
  5          n:=dump_csv( 'select 2 x, ''xxxx'' y, dummy from dual', ',', 'TMP', 't2.dat' );
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte@ORA9IR2> !ls -ltr /tmp/t?.dat
-rw-rw-r--  1 ora9ir2 ora9ir2 14 Sep 21 19:13 /tmp/t1.dat
-rw-rw-r--  1 ora9ir2 ora9ir2  9 Sep 21 19:13 /tmp/t2.dat


could be that your path or filename was invalid the second time around (and it wasn't that it was 
the second time, just the inputs were bad )  line 17 was a simple file open call 

5 stars sending a multiline query string to this function   September 28, 2005 - 4pm Central time zone
Reviewer: John K. from East Coast USA
I took the suggestion of a previous poster, making a wrapper procedure to the dump_cvs function. 
All works fine, even multi-line queries can be pasted into the procedure as I enter it in sql*plus. 


Is there a more direct way to enter the sql string parameter to the dmp_cvs function (in SQL*PLUS) 
when the sql string has multiple lines?  Much searching and posting to forums and experiments have 
not kept me from bumping into the limitation of pasting multi-line text into sql*plus. I looked at 
declaring a variable and trying to hide that in a scriptfile of some kind, calling it with 
something like @procedure_stub, tehn referring to the variable while calling the 
function/procedure, but found I am not experienced enough in PL/SQL to pull this off. 

I would ultimately like to be able to paste the (multi-line) query string into the command line in 
sql*plus.

Does one necessarily have to enter the procedure test_dump_csv in order execute the function 
dump_csv? 


Followup   September 28, 2005 - 5pm Central time zone:

not sure what you mean?

my example:

create or replace procedure test_dump_csv
as
    l_rows  number;
begin
    l_rows := dump_csv( 'select * 
                           from all_users 
                           where rownum < 25', 
                        ',', '/tmp', 'test.dat' );
end;
/

was a multi-line sql statement? 

5 stars   September 28, 2005 - 8pm Central time zone
Reviewer: John K. from East coast, USA
Sorry I was not clear. 

Is it possible to "parameterize" this procedure in such a way that one could, say, be prompted for 
the sql string (and paste it in), prompted for the field separator, prompted for the 
ORACLE_DIRECTORY name, and prompted for the filename? I imagine  being able to type something like

SQL> @dump_query

and then be able to paste in the query (and the other params) after a series of prompts. 
Right now I must type in the entire procedure, not just the parameters; that works as expected. But 
every time I try to paste in the multiple line query after being prompted (by a script that I have 
written based around your procedure), the errors are due to the line breaks in the pasted string. I 
am admittedly very green with variable substitution in sql*plus and pl/sql.

Thank you. 


Followup   September 29, 2005 - 6am Central time zone:

not really, not unless you end the lines to be glued together with "-", the sqlplus continuation 
character.

 

5 stars truncated fields using sqlldr_exp   October 11, 2005 - 2pm Central time zone
Reviewer: John K. from East Coast USA
While trying to unload a table into a text load file, I discovered that the fields of my table are 
being truncated; I don't think I am bumping into the limit your describe in your documentation:

•    There is an absolute limit of 2000 bytes in 7.x and 4000 bytes in 8.x per line/row for 
unloaded data.  The total size of the unloaded data is unlimited -- the maximum size of an 
individual row of data is what is limited.

I ran the utility, directing output to snoconso.ctl. Here is the "ctl" part of the file follwed by 
the first two lines of data:
LOAD DATA
INFILE *
INTO TABLE tmp_sno
REPLACE
FIELDS TERMINATED BY '|'
(
cui
,lat
,ts
,lui
,stt
,sui
,ispref
,aui
,saui
,scui
,sdui
,sab
,tty
,code
,str
,srl
,suppress
,cvf
)
BEGINDATA
C0004040|ENG|S|L0568373|PF|S0735588|Y|A3071096|102379017|61615004||SNOMEDCT|SY|61615004|Single-stran

C0004040|ENG|S|L1200216|PF|S1438948|Y|A3300297|102378013|61615004||SNOMEDCT|SY|61615004|Endonuclease


As you can see, the 15th field (str) is being truncated. Here is the desc of the table in question 
(formatted to fit this space:

CUI   NOT NULL CHAR(8)
LAT   NOT NULL CHAR(3)
TS    NOT NULL CHAR(1)
LUI   NOT NULL CHAR(8)
STT   NOT NULL VARCHAR2(3)
SUI   NOT NULL CHAR(8)
ISPREF   NOT NULL CHAR(1)
AUI   NOT NULL CHAR(8)
SAUI  VARCHAR2(50)
SCUI  VARCHAR2(50)
SDUI  VARCHAR2(50)
SAB   NOT NULL VARCHAR2(20)
TTY   NOT NULL VARCHAR2(20)
CODE  NOT NULL VARCHAR2(50)
STR   NOT NULL VARCHAR2(3000)
SRL   NOT NULL NUMBER(38)
SUPPRESS NOT NULL CHAR(1)
CVF   VARCHAR2(50)

How can I get the data into a .ctl file? 
The data in the dumptable was selected from a table originally loaded using sqlldr. In other words, 
the table I am dumping to text was created with:
 
create table todump
as select * from orig_table where sab = 'avalue'

Orig_table was loaded from a text file; here is the .ctl file for that:

options (direct=true, errors=0)
load data
CHARACTERSET UTF8
infile 'orig.RRF' "str X'7c0d0a'"
badfile 'orig.bad'
discardfile 'orig.dsc'
truncate
into table orig_table
fields terminated by '|'
trailing nullcols
(CUI    char(8),
LAT    char(3),
TS    char(1),
LUI    char(8),
STT    char(3),
SUI    char(8),
ISPREF    char(1),
AUI    char(8),
SAUI    char(50),
SCUI    char(50),
SDUI    char(50),
SAB    char(20),
TTY    char(20),
CODE    char(50),
STR    char(3000),
SRL    integer external,
SUPPRESS    char(1),
CVF    char(50)
)

 
While composing this letter, I used gawk to modify the original text file from which orig_table was 
loaded, created a .ctl file, and loaded it into a table on a different instance, which was my goal. 
But I am still curious why I could not get sqlldr_exp to work in this case. 


Followup   October 11, 2005 - 3pm Central time zone:

you'd have to supply me with an entire test case (inserts.....) 

what is in your login and glogin.sql files. 

4 stars using 9i   October 11, 2005 - 2pm Central time zone
Reviewer: John K. from East Coast USA
I forgot to mention that I am using 9i 


5 stars Reproducing the truncation   October 12, 2005 - 1pm Central time zone
Reviewer: John K. from East Coast USA
I was unable to reproduce the problem with a tiny dataset, but I can compare a spooled output of my 
existing table to the sqlldr_exp output. The spooled output is fine (but the cols have been 
formatted to suit these particular 19 rows). My login.sql is at the bottom of this post.

> set trimspool on
> set feedback off
> col str form a41
> col stt form a2
> col aui form a8
> col saui form a9
> col adui form a7
> col sdui form a7
> col sab form a8
> col tty form a3
> col code form a9
> col srl form 9
> col cvf form a3
> create table tmp_conso as select * from mrconso where rownum <20;
> select * from tmp_conso;
CUI      LAT T LUI      ST SUI      I AUI      SAUI      SCUI      SDUI    SAB      TTY CODE      
STR                                       SRL S CVF
-------- --- - -------- -- -------- - -------- --------- --------- ------- -------- --- --------- 
----------------------------------------- --- - ---
C0000005 ENG P L0000005 PF S0007492 Y A7755565           M0019694  D012711 MSH      PEN D012711   
(131)I-Macroaggregated Albumin              0 N
C0000005 ENG S L0270109 PF S0007491 Y A0016458           M0019694  D012711 MSH      EN  D012711   
(131)I-MAA                                  0 N
C0000039 CZE P L3180523 PF S3708014 Y A3909890                     D015060 MSHCZE   MH  D015060   
1,2-DIPALMITOYLFOSFATIDYLCHOLIN             3 N
C0000039 ENG P L0000039 PF S0007564 N A6326244           C25778            NDFRT    IN  C25778    
1,2-Dipalmitoylphosphatidylcholine          0 N 256
C0000039 ENG P L0000039 PF S0007564 Y A0016515           M0023172  D015060 MSH      MH  D015060   
1,2-Dipalmitoylphosphatidylcholine          0 N 256
C0000039 ENG P L0000039 VO S1357296 Y A1317708           M0023172  D015060 MSH      PM  D015060   
1,2 Dipalmitoylphosphatidylcholine          0 N
C0000039 ENG S L0000035 PF S0007560 Y A0016511           M0023172  D015060 MSH      EN  D015060   
1,2-Dihexadecyl-sn-Glycerophosphocholine    0 N
C0000039 ENG S L0000035 VO S1357276 Y A1317687           M0023172  D015060 MSH      PM  D015060   
1,2 Dihexadecyl sn Glycerophosphocholine    0 N
C0000039 ENG S L0000038 PF S0007563 Y A0016514           M0023172  D015060 MSH      EN  D015060   
1,2-Dipalmitoyl-Glycerophosphocholine       0 N
C0000039 ENG S L0000038 VO S1357295 Y A1317707           M0023172  D015060 MSH      PM  D015060   
1,2 Dipalmitoyl Glycerophosphocholine       0 N
C0000039 ENG S L0012507 PF S0033298 N A0049238                             SNMI     PT  F-63675   
Dipalmitoylphosphatidylcholine              4 N 256
C0000039 ENG S L0012507 PF S0033298 N A2880749 166113012 102735002         SNOMEDCT PT  102735002 
Dipalmitoylphosphatidylcholine              4 N 256
C0000039 ENG S L0012507 PF S0033298 Y A0049237           M0023172  D015060 MSH      EN  D015060   
Dipalmitoylphosphatidylcholine              0 N 256
C0000039 ENG S L0012507 VC S0627555 N A0683490                             LNC      CN  NOCODE    
DIPALMITOYLPHOSPHATIDYLCHOLINE              0 N
C0000039 ENG S L0012507 VC S0627555 Y A6841046                             LNC      LPN LP15542   
DIPALMITOYLPHOSPHATIDYLCHOLINE              0 N
C0000039 ENG S L0012508 PF S0033296 Y A0049235           M0023172  D015060 MSH      EN  D015060   
Dipalmitoylglycerophosphocholine            0 N 256
C0000039 ENG S L0012509 PF S0033297 Y A0049236           M0023172  D015060 MSH      EP  D015060   
Dipalmitoyllecithin                         0 N 256
C0000039 ENG S L0296452 PF S0033295 Y A0049234           M0023172  D015060 MSH      EN  D015060   
Dipalmitoyl Phosphatidylcholine             0 N 256
C0000039 ENG S L0296452 VW S0073244 Y A0100864           M0023172  D015060 MSH      PM  D015060   
Phosphatidylcholine, Dipalmitoyl            0 N
> spool off
 

The sql_exp output is truncated:
LOAD DATA
INFILE *
INTO TABLE tmp_conso
REPLACE
FIELDS TERMINATED BY '|'
(
cui
,lat
,ts
,lui
,stt
,sui
,ispref
,aui
,saui
,scui
,sdui
,sab
,tty
,code
,str
,srl
,suppress
,cvf
)
BEGINDATA
C0000005|ENG|P|L0000005|PF|S0007492|Y|A7755565||M0019694|D012711|MSH|PEN|D012711|(131)I-Macroaggrega

C0000005|ENG|S|L0270109|PF|S0007491|Y|A0016458||M0019694|D012711|MSH|EN|D012711|(131)I-MAA|0|N|
C0000039|CZE|P|L3180523|PF|S3708014|Y|A3909890|||D015060|MSHCZE|MH|D015060|1,2-DIPALMITOYLFOSFATIDYL

C0000039|ENG|P|L0000039|PF|S0007564|N|A6326244||C25778||NDFRT|IN|C25778|1,2-Dipalmitoylphosphatidylc

C0000039|ENG|P|L0000039|PF|S0007564|Y|A0016515||M0023172|D015060|MSH|MH|D015060|1,2-Dipalmitoylphosp

C0000039|ENG|P|L0000039|VO|S1357296|Y|A1317708||M0023172|D015060|MSH|PM|D015060|1,2 
Dipalmitoylphosp
C0000039|ENG|S|L0000035|PF|S0007560|Y|A0016511||M0023172|D015060|MSH|EN|D015060|1,2-Dihexadecyl-sn-G

C0000039|ENG|S|L0000035|VO|S1357276|Y|A1317687||M0023172|D015060|MSH|PM|D015060|1,2 Dihexadecyl sn 
G
C0000039|ENG|S|L0000038|PF|S0007563|Y|A0016514||M0023172|D015060|MSH|EN|D015060|1,2-Dipalmitoyl-Glyc

C0000039|ENG|S|L0000038|VO|S1357295|Y|A1317707||M0023172|D015060|MSH|PM|D015060|1,2 Dipalmitoyl 
Glyc
C0000039|ENG|S|L0012507|PF|S0033298|N|A0049238||||SNMI|PT|F-63675|Dipalmitoylphosphatidylcholine|4|N

C0000039|ENG|S|L0012507|PF|S0033298|N|A2880749|166113012|102735002||SNOMEDCT|PT|102735002|Dipalmitoy

C0000039|ENG|S|L0012507|PF|S0033298|Y|A0049237||M0023172|D015060|MSH|EN|D015060|Dipalmitoylphosphati

C0000039|ENG|S|L0012507|VC|S0627555|N|A0683490||||LNC|CN|NOCODE|DIPALMITOYLPHOSPHATIDYLCHOLINE|0|N|
C0000039|ENG|S|L0012507|VC|S0627555|Y|A6841046||||LNC|LPN|LP15542|DIPALMITOYLPHOSPHATIDYLCHOLINE|0|N

C0000039|ENG|S|L0012508|PF|S0033296|Y|A0049235||M0023172|D015060|MSH|EN|D015060|Dipalmitoylglyceroph

C0000039|ENG|S|L0012509|PF|S0033297|Y|A0049236||M0023172|D015060|MSH|EP|D015060|Dipalmitoyllecithin|

C0000039|ENG|S|L0296452|PF|S0033295|Y|A0049234||M0023172|D015060|MSH|EN|D015060|Dipalmitoyl 
Phosphat
C0000039|ENG|S|L0296452|VW|S0073244|Y|A0100864||M0023172|D015060|MSH|PM|D015060|Phosphatidylcholine,


login.sql:
--  login.sql
--
-- DESCRIPTION
--   SQL*Plus login startup file.
--
--   Add any sqlplus commands here that are to be executed when a user
--   starts SQL*Plus on your system
--
-- USAGE
--   This script is automatically run when SQL*Plus starts
--

DEFINE _EDITOR='gvim -c "set filetype=sql"'
-- For backward compatibility

SET SQLPLUSCOMPATIBILITY 8.1.7


set serveroutput on size 1000000 FORMAT WRAPPED
set trimspool on
set long 5000
set linesize 300
set pagesize 999
column plan_plus_exp format a80
column global_name new_value gname
set termout off

define gname=idle
column global_name new_value gname
select lower(user) || '@' || substr( global_name, 1,
decode( dot, 0, length(global_name), dot-1) ) global_name
from (select global_name, instr(global_name,'.') dot from global_name );
set sqlprompt '&gname> '

set termout on

-- Used by Trusted Oracle
COLUMN ROWLABEL FORMAT A15

-- Used for the SHOW ERRORS command
COLUMN LINE/COL FORMAT A8
COLUMN ERROR    FORMAT A65  WORD_WRAPPED

-- Used for the SHOW SGA command
COLUMN name_col_plus_show_sga FORMAT a24

-- Defaults for SHOW PARAMETERS
COLUMN name_col_plus_show_param FORMAT a36 HEADING NAME
COLUMN value_col_plus_show_param FORMAT a30 HEADING VALUE

-- Defaults for SET AUTOTRACE EXPLAIN report
COLUMN id_plus_exp FORMAT 990 HEADING i
COLUMN parent_id_plus_exp FORMAT 990 HEADING p
COLUMN plan_plus_exp FORMAT a60
COLUMN object_node_plus_exp FORMAT a8
COLUMN other_tag_plus_exp FORMAT a29
COLUMN other_plus_exp FORMAT a44

set editfile Q:\scripts\oracle\current.buf 


Followup   October 13, 2005 - 9am Central time zone:

but all sqlldr_exp does is create a script that spools - nothing more.


look at sqlldr_exp - see that is just does a start /tmp/flat$$.sql -- grab /tmp/flat$$.sql and see 
if you can see any reason it would do that

see what is different from flat$$.sql (the $$ is replaced with a number by the shell of course) 
from your spool script.

I've not had this happen to me.


for example, if I run

$ sqlldr_exp scott/tiger dept


/tmp/flat$$ will look like this:


prompt LOAD DATA
prompt INFILE *
prompt INTO TABLE dept
prompt REPLACE
prompt FIELDS TERMINATED BY '|'
prompt (
prompt deptno
prompt ,dname
prompt ,loc
prompt )
prompt BEGINDATA
select
deptno||chr(124)||
dname||chr(124)||
loc
from    dept
/
exit



and that script is run by this:

sqlplus -s $1 << EOF
set wrap off
set feedback off
set pagesize 0
set verify off
set trimspool on
set linesize 5000
start /tmp/flat$$.sql
exit
EOF
 

4 stars exporting file   October 30, 2005 - 7am Central time zone
Reviewer: jp from Dubai
Tom, 

I am trying to export information into a csv file,  but when I run the test_dump_csv I am getting 
an error

  1  create or replace procedure test_dump_csv
  2  as
  3      l_rows  number;
  4  begin
  5      l_rows := dump_csv( 'select *
  6                             from scott.dept',
  7                          ',', 'c:\', 'test.dat' );
  8* end;
SQL> /

Procedure created.

SQL> exec test_dump_csv
BEGIN test_dump_csv; END;

*
ERROR at line 1:
ORA-29280: invalid directory path
ORA-06512: at "SYS.UTL_FILE", line 18
ORA-06512: at "SYS.UTL_FILE", line 424
ORA-06512: at "SYS.DUMP_CSV", line 17
ORA-06512: at "SYS.TEST_DUMP_CSV", line 5
ORA-06512: at line 1

Even I tried creating a directory but still same error,

SQL> create or replace directory myfile as 'c:\tmp'
  2  /

Directory created.

  1   create or replace procedure test_dump_csv
  2   as
  3       l_rows  number;
  4   begin
  5       l_rows := dump_csv( 'select *
  6                              from scott.dept',
  7                           ',', 'myfile', 'test.dat' );
  8*  end;
SQL> /

Procedure created.

SQL> exec test_dump_csv
BEGIN test_dump_csv; END;

*
ERROR at line 1:
ORA-29280: invalid directory path
ORA-06512: at "SYS.UTL_FILE", line 18
ORA-06512: at "SYS.UTL_FILE", line 424
ORA-06512: at "SYS.DUMP_CSV", line 17
ORA-06512: at "SYS.TEST_DUMP_CSV", line 5
ORA-06512: at line 1


Can you let me know what its the problem?

thanks
 


Followup   October 31, 2005 - 2am Central time zone:

[tkyte@localhost ~]$ oerr ora 29280
29280, 00000, "invalid directory path"
// *Cause:  A corresponding directory object does not exist.
// *Action: Correct the directory object parameter, or create a corresponding
//          directory object with the CREATE DIRECTORY command.
[tkyte@localhost ~]$


directory names would be case sensitive and identifiers are folded to upper case

  5       l_rows := dump_csv( 'select *
  6                              from scott.dept',
  7                           ',', 'MYFILE', 'test.dat' );
                                    ^^^^^^

In the first case, utl_file_dir wasn't set probably (good thing too - I would not want you to WRITE 
to my c:\ drive on the SERVER!!!!  utl_file always writes to the SERVER) 

4 stars exporting file - fixed   October 30, 2005 - 11am Central time zone
Reviewer: jp from Dubai
Tom,

I found the problem,  I did not set the utl_file_dir value in the init.ora
its working fine.

but I have another question, can I create a export file but separate by tab,  something like
10  ACCOUNTING     NEW YORK
20  RESEARCH       DALLAS etc..

thanks for your help

 


Followup   October 31, 2005 - 2am Central time zone:

chr(9)

instead of

','

will use tabs. 

5 stars date format   November 3, 2005 - 9am Central time zone
Reviewer: jp from Dubai
Tom,

thanks, the char(9) works just perfect,

I tried to run the following query in the test_dump_csv

select TO_CHAR(insert_date, 'YYYYMMDD'),room_category,tot_room,scala_code from apart_stat

but its compiling with erros,  the error seems to be related to TO_CHAR(insert_date,'YYYYMMDD'),

Is there any restriction for date format?

I tested the query in sqlplus and worked fine
thanks,.
 


Followup   November 4, 2005 - 2am Central time zone:

no, but you don't show us the example, so I'll assume you forgot to use '' to get a single ' in a 
character string literal. 

1 stars I want java code for data given by user which should be store/retrieve in/from flat file   December 22, 2005 - 1am Central time zone
Reviewer: chandrachary from India


Followup   December 22, 2005 - 10am Central time zone:

cool?

shouldn't be very hard for a java programmer to write I wouldn't expect... You have the logic 
virtually outlined for you right above, just have to change it to "result sets" 

5 stars Question   December 30, 2005 - 10am Central time zone
Reviewer: PRS from USA
Tom,
   Can I use directory object to create file using UTL_FILE on a different server(Application 
Server) than the database server?  It does not create the file on my application server. Rather it 
creates the file on the directory used in create directory object on the database server.
Thanks,
PRS     


Followup   December 30, 2005 - 11am Central time zone:

only if the database server can SEE that directly itself

otherwise, it would be very virus like, wouldn't it.  How would you like it if files just started 
appearing on your machine magically??? 

4 stars enlightning   January 7, 2006 - 5am Central time zone
Reviewer: kirtish from Pune, India
Hi Tom,

Its useful as ever. You always learn new things.
I was going though the above posts, i had a doubt if i can 
create a file in a directory through a "after insert trigger" and how?

Thanks 
kirtish 


Followup   January 7, 2006 - 10am Central time zone:

utl_file is used to create files.

HOWEVER, before you do anything NON-TRANSACTIONAL in a trigger - make sure you know what you are 
doing.

Say you create that file in the after insert trigger....
And the transaction is rolled back.

Files do not rollback.

Be very very very careful using any routine that starts with UTL_ in a trigger - utl_file, 
utl_smtp, utl_http, utl_tcp, whatever - they are typically NON-TRANSACTIONAL - they will NOT 
rollback when the transaction rolls back which can lead to a *mess*. 

5 stars Static SQL   March 20, 2006 - 11pm Central time zone
Reviewer: Preeti from USA
Tom,

I searched on your website, I couldn't find an example of utl_file using static sql. You did 
mention in your article that if we have static sql we could avoid the dbms_sql.parse and hence the 
code would be faster. Could you please provide an example. 

Also, is it possible to declare the sql in a cursor rather than putting it in a varchar2 variable.

Thanks 


Followup   March 22, 2006 - 1pm Central time zone:

utl_file.fopen
for x in ( select * from emp )
loop
   utl_file.put_line( x.ename || ',' || x.empno || .... );
end loop;


???

where I do dynamic, you do static - no "magic", as straightforward as it sounds.


If you want a generic routine as I have provided, you would use dbms_sql and dbms_sql does not 
accept ref cursors.
 

4 stars Better UTL_FILE performance   May 29, 2006 - 4am Central time zone
Reviewer: Andrew from Singapore
What I found gives significantly better performance in UTL_FILE is if you concatenate all your 
SELECT columns into a single VARCHAR2 column (if you can afford to have a limit of 4000 chars per 
line). You can then have a generic stored procedure which takes in any native cursor variable with 
a single VARCHAR2 field and which pumps the cursor contents out to a file. If I remember correctly 
it performed about 50% faster than a procedure which  had about 10 columns doing the same work.

A question for Tom - presuming you could ensure strictly single-thread access to an external DLL, 
would the performance of an export-single-column DLL be very fast when called from extproc (within 
PL/SQL)? What we need is a fast solution which can be invoked from the server, rather than relying 
on shell scripts and jobs. 


Followup   May 29, 2006 - 7am Central time zone:

Or....

you could select out all columns and have the data dumper build the string (which would make more 
sense...)

You would likely find an external procedure written in C to be "faster", yes. 

5 stars reading multiple lines of a file   August 24, 2006 - 10am Central time zone
Reviewer: Paulo from Portugal
Hi Tom

In the past you help me, now i need once more a little help from you.
I have a file that's contained 4 lines of information, in each line there is several information 
that is separeted by "|". My problem is this, my plsql reads perfectlly the first line but i cannot 
read the second, i don't know how. Can you tell me how i can read the other lines? If possible with 
an example.
I'm using a utl_file to work with the files 


Followup   August 27, 2006 - 7pm Central time zone:

if you know how to read the first line, why can't you read the second? 

the code to read the "second" line is identical to the code to read the first line. 

5 stars PAGESIZE LIMIT?   August 29, 2006 - 6am Central time zone
Reviewer: abz 
I have a simple query whose data I want to spool in a file, using SQLPLUS SPOOL, the problem is 
that maximum page size can be of 50000. What I did is.
SET LINESIZE 3000;
SET HEAD OFF;
SET PAGESIZE 50000

everything is fine in the output file but after each 50000 lines SQLPLUS
adds a BLANK LINE in the output,

How can I solve this problem 


Followup   August 29, 2006 - 7am Central time zone:

SQL> set pagesize 0

http://download-east.oracle.com/docs/cd/B19306_01/server.102/b14357/ch12040.htm#sthref2845

4 stars a little bit confusing syntax   August 30, 2006 - 2am Central time zone
Reviewer: abz 
Thanks that solved the problem.

1- But the syntax that is SET PAGESIZE 0 is not so intuitive to human being, shouldnt it be SET 
PAGESIZE unlimited or infinite? that would be more intuitive.

2- One more problem, in the end of the query it give a message "xx rows selected", how can I avoid 
this? 


Followup   August 30, 2006 - 8am Central time zone:

1) that would go against what the documentation states :)  Intuitive is entirely in the eye of the 
beholder here.  In order to see how something works, I read the documentation.

2) back to the docs:

http://download-east.oracle.com/docs/cd/B19306_01/server.102/b14357/ch12040.htm#sthref2764
set feedback off 

5 stars i got the answer for 2   August 30, 2006 - 5am Central time zone
Reviewer: abz 
I got the answer for 2, can you please comment on 1.

SET FEED[BACK] {6|n|ON|OFF}
Displays the number of records returned by a script when a script selects at least n records. ON or 
OFF turns this display on or off. Turning feedback ON sets n to 1. Setting feedback to zero is 
equivalent to turning it OFF.

 


Followup   August 30, 2006 - 8am Central time zone:

already did.... 

4 stars Can UTL_FILE package used to create ASCII files   August 30, 2006 - 7am Central time zone
Reviewer: Karthik from India
I have one problem in creating csv file.                                                            
                                                                  
If one column has single line value, it is coming in single cell. But if the column has no.of lines 
using carriage return while entering into the table,              
I am not able to create csv file properly. That one column value takes more than one cell in csv.   
                                                                  
For example the column "Issue" has following value:                                                 
                                                                  
 "Dear                                                                                              
                                                                  
 I hereby updated the Human Resources: New User Registration Form Request.                          
                                                                  
 And sending the request for your action.                                                           
                                                                  
 Regards                                                                                            
                                                                  
 Karthik".                                                                                          
                                                                  
                                                                                                    
                                                                  
 If i try to create the csv file that particular record is coming as follows:                       
                                                                  
                                                                                                    
                                                                  
 0608001,AEGIS USERID,SINGAPORE, Dear                                                               
                                                                  
                                 I hereby updated the Human Resources: New User Registration Form 
Request.                                                            
                 And sending the request for your action.                                           
                                                  
                 Regards                                                                            
                                                  
                 Karthik,Closed.                                                                    
                                                  
                                                                                                    
                                                  
If we try to load the data in table it is giving error since that one record is coming in more than 
one line. How can I store that value in a single line in csv file.
Pls help.                                                                                           
                                                   
 


Followup   August 30, 2006 - 8am Central time zone:

you tell me, what would YOU like the file to look like?

once you tell us that, we can show you how to do that. 

4 stars extracting data to a flat file   August 31, 2006 - 1am Central time zone
Reviewer: karthik from India
The CSV file is being generated by this pl/sql for one record in many lines like:
0608001,AEGIS USERID,SINGAPORE, Dear 
I hereby updated the Human Resources          
Regards
Karthik,Closed.


But I need everything in one line.
0608001,AEGIS,DEAR I hereby updated the HumanResources Regards Karthik,Closed
                                                                                                   

 


Followup   August 31, 2006 - 9am Central time zone:

use translate to replace chr(13) and chr(10) with ' '

select ... translate(column, chr(13)||chr(10), '  ' ) .....


that'll turn carriage returns and linefeeds into spaces, changing the data of course, but that is 
what you asked for. 

5 stars extracting data to a flat file   September 1, 2006 - 12am Central time zone
Reviewer: Karthik from India
Gr8....That only I was looking for.....It is working fine.
But I want to ask one more question. I can make the csv file using translate. But If I want to load 
the data in that exact format without modifying the data(including carriage returns and line 
feeds), how can I load from one database to another. I dont have permission to use databaselink. Is 
there any other way to load? If it is there then how? Pls explain 


Followup   September 1, 2006 - 8am Central time zone:

what tool will you use? 

4 stars extracting data to a flat file   September 1, 2006 - 8am Central time zone
Reviewer: karthik from India
I am using SQL LOADER.  


Followup   September 1, 2006 - 10am Central time zone:

if you are doing oracle to oracle, I'm seriously wondering why you are writing code.

exp
imp

code free, be done with it.  you are finished (take an early weekend!)


but if you persist in the "do it yourself", use this format of a control file and leave the 
newlines in the data - use a record terminator instead.  


<quote src=Expert Oracle database architecture>
Use the STR Attribute

This is perhaps the most flexible method of loading data with embedded newlines. Using the STR 
attribute, we can specify a new end-of-line character (or sequence of characters). This allows us 
to create an input data file that has some special character at the end of each line—the newline is 
no longer “special.”
I prefer to use a sequence of characters, typically some special marker, and then a newline. This 
makes it easy to see the end-of-line character when viewing the input data in a text editor or some 
utility, as each record still has a newline at the end of it. The STR attribute is specified in 
hexadecimal, and perhaps the easiest way to get the exact hexadecimal string we need is to use SQL 
and UTL_RAW to produce the hexadecimal string for us. For example, assuming we are on UNIX where 
the end-of-line marker is CHR(10) (linefeed) and our special marker character is a pipe symbol (|), 
we can write this:

ops$tkyte@ORA10G> select utl_raw.cast_to_raw( '|'||chr(10) ) from dual;
 
UTL_RAW.CAST_TO_RAW('|'||CHR(10))
-------------------------------------------------------------------------------
7C0A
which shows us that the STR we need to use on UNIX is X'7C0A'. 


Note    On Windows, you would use UTL_RAW.CAST_TO_RAW( '|'||chr(13)||chr(10) ).


To use this, we might have a control file like this:

LOAD DATA
INFILE demo.dat "str X'7C0A'"
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ','
TRAILING NULLCOLS
(DEPTNO,
  DNAME        "upper(:dname)",
  LOC          "upper(:loc)",
  COMMENTS
)

So, if our input data looks like this:

[tkyte@desktop tkyte]$ cat demo.dat
10,Sales,Virginia,This is the Sales
Office in Virginia|
20,Accounting,Virginia,This is the Accounting
Office in Virginia|
30,Consulting,Virginia,This is the Consulting
Office in Virginia|
40,Finance,Virginia,This is the Finance
Office in Virginia|
[tkyte@desktop tkyte]$

where each record in the data file ends with a |\n, the previous control file will load it 
correctly.
</quote> 

4 stars Can we use dbms_output to dump entire row from cursor?   September 13, 2006 - 11am Central time zone
Reviewer: Robert from Memphis, USA
Tom,

Is there a simple way (perhaps using refcursor) from within a procedure, to write out an entire row 
from a query with DBMS_OUTPUT?

I know I can string together the columns, but I want a method that is as simple as doing a 'select' 
in sql*plus... *without* having to do a PRINT :X after running the procedure... only using 
dbms_output.put_line.

Thanks,

Robert. 


Followup   September 13, 2006 - 3pm Central time zone:

you have to string them together. 

5 stars Need your help   September 13, 2006 - 10pm Central time zone
Reviewer: ND from USA
Hi Tom,
This post is very helpful to my current project.  I want to thank you for that.  Also I have a 
quick question on the last define_column statement from your original post:

dbms_sql.define_column( l_theCursor, 1, l_columnValue, 2000 );

what does it do?  Didn't we already define all the columns from inside the loop??

Thanks for your time,
PS: I am sure you have some Oracle-related books publised, can you recommend me one? 


Followup   September 14, 2006 - 9am Central time zone:

typo, not needed. 

4 stars Excellent....! but incomplete   September 21, 2006 - 2am Central time zone
Reviewer: Venkataramesh K from Hyderabad, AP, INDIA
Nice one, found it very useful. But the script is not handling  the columns having the data with 
whole spaces. It is printing NULL for columns has spaces in it. 


Followup   September 21, 2006 - 2am Central time zone:

"having the data with whole spaces"

eh?  sorry, but no clue what you mean - however I can say "you have the code, fix it". 

3 stars Description   September 21, 2006 - 2am Central time zone
Reviewer: Venkataramesh from Hyd, AP, India
hi,

sorry for not explaining correctly.

Actually i have a column of data type varchar2(4). The value that was stored in this field is '    
' (Four spaces). When i use this script to generate a file, the output is printing null instead of 
four spaces in the file. 

Appreciate your help

Thanks in advance. 


Followup   September 21, 2006 - 6am Central time zone:

ops$tkyte%ORA10GR2> create table t ( x int, y varchar2(4), z int );

Table created.

ops$tkyte%ORA10GR2> insert into t values ( 1, '    ', 2 );

1 row created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create or replace procedure test_dump_csv
  2  as
  3      l_rows  number;
  4  begin
  5      l_rows := dump_csv( 'select * from t',
  6                          ',', 'MY_DIR', 'test.dat' );
  7  end;
  8  /

Procedure created.

ops$tkyte%ORA10GR2> exec test_dump_csv;

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> !cat /tmp/test.dat
1,    ,2


works for me, are you "sure".  I used 10gr2 - what about you. 

4 stars One more doubt   September 22, 2006 - 2am Central time zone
Reviewer: venkataramesh from Hyd, India
I am also using 10gr2 version.

It is working for me. 

Now i had another dount, i had a char(4) column which is nullable and having a null value in it. I 
executed the process. 
 Name       Null?    Type
----------- -------- ----------------------------
 X                   NUMBER(38)
 Y                   CHAR(4)
 Z                   NUMBER(38)
The result from the select looks like the below.

         X Y             Z
---------- ---- ----------
         1               2
why did y was given space in the select query result. the export dump looks like the below. (which 
is correct)
SQL> !cat temp.dat
12

Now my doubt is why the select query is showing four spaces.

 


Followup   September 22, 2006 - 3pm Central time zone:

well, why don't you do what i did...

post an ENTIRE example....

ops$tkyte%ORA10GR1> create table t ( x number, y char(4), z number );

Table created.

ops$tkyte%ORA10GR1> insert into t values ( 1, '    ', 2 );

1 row created.

ops$tkyte%ORA10GR1> create or replace procedure test_dump_csv
  2  as
  3      l_rows  number;
  4  begin
  5      l_rows := dump_csv( 'select * from t',
  6                          ',', 'MY_DIR', 'test.dat' );
  7  end;
  8  /

Procedure created.

ops$tkyte%ORA10GR1>
ops$tkyte%ORA10GR1> exec test_dump_csv;

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR1> !cat /tmp/test.dat
1,    ,2
 

4 stars Problem with the DBMS_SQL with following query   October 12, 2006 - 5am Central time zone
Reviewer: Venkataramesh K from Hyderabad, AP, India
I have given the input query to the procedure which unloads the data to a flat file as follows. 

select sysdate,'00000000' from dual

Now i am getting the following error.

ERROR at line 1:
ORA-06550: line 1, column 101:
PLS-00103: Encountered the symbol "00000" when expecting one of the following:
) , * & | = - + < / > at in is mod remainder not rem => ..
<an exponent (**)> <> or != or ~= >= <= <> and or like LIKE2_
LIKE4_ LIKEC_ between || multiset member SUBMULTISET_
The symbol ", was inserted before "00000" to continue.
 


Followup   October 12, 2006 - 8am Central time zone:

why do you not show us your code, like I show you my code, so we can see what you are doing wrong?

ops$tkyte%ORA10GR2> create or replace procedure test_dump_csv
  2  as
  3      l_rows  number;
  4  begin
  5      l_rows := dump_csv( 'select sysdate,''000000'' from dual', ',', 'MY_DIR' , 'x.dat' );
  6  end;
  7  /

Procedure created.

ops$tkyte%ORA10GR2> exec test_dump_csv

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> !cat /tmp/x.dat
12-OCT-06,000000


you probably didn't do quotes correctly. 

4 stars Another option using SQL+ & COLSEP !!!   October 18, 2006 - 3pm Central time zone
Reviewer: george lewycky from New JErsey, USA


SET SERVEROUTPUT ON
SET ECHO OFF
SET VERIFY OFF
SET Heading OFF
SET LINESIZE 2000
SET NEWPAGE NONE
SET PAGESIZE 100
SET Heading OFF
SET COLSEP ,            <-  this saves you the headache of coding each column!!!
spool c:\myfile.txt
select * from tablename        <----   select all from your table
spool off

NOTE: if any of the columns have ',' embedded in them like the address you might be in a bit of a 
bind. So you might need a unique delimiter like '|' or ']' 
    You must include tick marks with this delimiter !!!   See below line:

             SET COLSEP '|'     or  SET COLSEP ']'   

your file will look something like this:    
92877|S SHECTER         |EMGINEERING & TECHNICAL FIELD
92881|S SHECTER         |ENGINEERING & TECHNICAL FIELD
 


3 stars RE: Another option using SQL+ & COLSEP !!!   October 27, 2006 - 9am Central time zone
Reviewer: Maarten from The Netherlands
TO: george lewycky

How can I lose the spaces between
SHECTER and |EMGINEERING    

in your file:    
92877|S SHECTER         |EMGINEERING & TECHNICAL FIELD
92881|S SHECTER         |ENGINEERING & TECHNICAL FIELD
 


 


4 stars Replacing table Data if condition satisfied   January 12, 2007 - 1am Central time zone
Reviewer: Rinku 
Hi Tom,
In addition to loading the data from the table to a file, I want to replace some data in a column to some other value in the file, i.e. lets say in a column if a entry is '0' I want to replace it by 'false' in the file (column & table remains unchanged) and if it is '1' replace it by 'true'.
I hope I was clear

Regards
Rinku

3 stars getting error   January 15, 2007 - 8am Central time zone
Reviewer: Murali from Toronto
Hi Tom,

I copied exactly the same procedure.. in apps instance..
and trying execute it is giving error as follow

and running in Toad n tried in unix prompt as well
===========================================
declare
  l_rows number;
begin
  l_rows := dump_csv( 'select *
                  from all_users
                  where rownum < 25',
                ',', '/tmp', 'test.dat' );
END ;

========================================
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "SYS.UTL_FILE", line 98
ORA-06512: at "SYS.UTL_FILE", line 157
ORA-06512: at "APPS.DUMP_CSV", line 16
ORA-06512: at line 4
what need to be done..
please let me know..






4 stars Want to preserve trailing spaces   January 18, 2007 - 6pm Central time zone
Reviewer: Eric Schneider from Columbus, OH
Tom,

I need to extract a table to a fixed length flat file. The first column is 3 characters and second column will be truncated to 25 characters. The problem I'm having is that if the second column ends with spaces, those spaces are not written to the file.

001ONE TWO THREE       
003THIS IS TEST DATA   
A12I AM TESTING THE CUT OFF
090BLUE CROSS AND BLUE SHIEL

If I turn trimspool and trimout off, the end of line character is at the 80th character within the output file, and I need it to be at the 25th.

Here is the simple code:

set heading off
set colsep ''
set feedback off


SPOOL &1


SELECT SUBSTR(cde_carrier, 5, 7),
    RPAD(SUBSTR(nam_bus, 1, 25),25,' ')
FROM t_tpl_carrier;

SPOOL OFF;


Thank you,
Eric


4 stars pipelining the output for a procedure   January 25, 2007 - 10am Central time zone
Reviewer: Valli from Bangalore, India
Hi Tom,
I had been referring to this site for around 2 years now, but never found a need to ask a new question or try to follow-up on something... It just has answers for everything... Great work!!!
But I have got an interesting problem now...one part or another of which doesnt look like getting answered anywhere...
I have a procedure which updates a few databases using native dynamic sql. Its a kind of transaction processing and i need to log each of the transactions. I cant use utl_file as the log has to be put in an application server and not the DB server. And i cannot wait for the entire process to end before the logs get updated. So spooling is ruled out. And one of the links lead me to pipelined functions.. which could not be used as i am doing data manipulation here.

Currently, i am trying to call the procedure by limiting the input data to be processed to 100 rows and getting the logging and putting it to a log file and call for another 100 rows and so on, and building the log file using a shellscript.
But i get a feeling there is a better way and you would be able to help!! Any suggestions Tom??

5 stars separator   February 5, 2007 - 10am Central time zone
Reviewer: shubham from India Mumbai
Hi Tom,
above you have specified to use " in case the separator is already present in the columns.
can we not get a .csv file with fields enclosed in ".
like
rather then -
i,am,shubham
i would like to have -
"i","am","shubham"

is it possible....

thanx

Followup   February 5, 2007 - 10am Central time zone:

you have the code, you can make the code do whatever you would like it to do.

So, "is it possible..." sure - right after you make the very minor change to the code to do it!
4 stars separator   February 5, 2007 - 10am Central time zone
Reviewer: shubham from india mumbai
i m looking for that very change....
like..wot change shud i make to the above DUMP_CSV function

thanks a lot...

Followup   February 5, 2007 - 11am Central time zone:

you really cannot figure that out?

instead of

a,b,c,d

you want

"a","b","c","d"

and give the above really simple code - cannot do it??


wuh-oh.

utl_file.put( l_output, l_separator ||
                        l_columnValue );

could become

utl_file.put( l_output, l_separator ||
                        '"' || l_columnValue || '"' );


and if you think l_columnValue might have " in it an needs to be doubled up:

utl_file.put( l_output, l_separator ||
              '"' || replace(l_columnValue,'"','""') || '"' );

or escaped with a \ (depends on what is reading this file)


utl_file.put( l_output, l_separator ||
              '"' || replace(l_columnValue,'"','\"') || '"' );



3 stars separator   February 6, 2007 - 5am Central time zone
Reviewer: shubham from mum india
thanx for help....


5 stars spool   February 8, 2007 - 10am Central time zone
Reviewer: John from CA
i need to create 2 comma delimited
files(extracts) from a table(one for each fiscal week)..based on the day i am running it has to be for the previous 2 weeks...the sql is pretty simple..i'll call it from UNIX


table a
-----
fy_week ,
cust,
pkg,
delvery,
date
sales_dollars



I can figure out the weeks by running the following query from calendar_dim

select
fiscal_year_week_prior1,
fiscal_year_week_prior2
from
calendar_dim
where fy_date = $1

then for fiscal_year_week_prior1 and fiscal_year_week_prior2 i need to run the following script
to spool 2 files each

select
fy_week||','||cust||','||pkg||','||delivery||','||date||','||sales_dollars
where fy_week = v_week


The only part i need help is the file name that i am spooling has to have fiscal week to be a part of it..


like...

file_1_yyyywk.dat

file_2_yyyywk.dat

basically i will pass in sysdate to a unix script and the calendar_dim query will figure out what weeks to
run and i have to spool the two files based on the first query for each week and name the files with fy_week part of the file name...any ideas...



Followup   February 8, 2007 - 11am Central time zone:

you pass in the date right - so just the same way you referenced it in your query, reference it in your spool command.
5 stars   February 8, 2007 - 12pm Central time zone
Reviewer: John from CA
But i have to run the SQL to determine the weeks.how would i do that? can you please give me an example showing that?

Followup   February 8, 2007 - 1pm Central time zone:

column fy1 new_val fy1
column fy2 new_val fy2

select
fiscal_year_week_prior1 fy1,
fiscal_year_week_prior2 fy2
from
calendar_dim
where fy_date = $1;

spool &fy1..dat
.....
spool off

spool &fy2..dat
.....
spool off
5 stars text-->sqlldr -->flat -->text problem   February 9, 2007 - 9am Central time zone
Reviewer: John Kilbourne from DC area
I loaded a text file into oracle and later retrieved a view, and found that a long string was truncated. After some tweaking, I have a test case that reproduces this. Notice the end of the tmp.flat file; the string is truncated and ends in "SALSO", and the other fields are just gone. I include my login.sql, as I thought perhaps the answer was there, but I don't see it.

The string is not truncated in the table. The BEGINDATA part of the .ctl file is all one line in the file.

tmp.sql:
DROP TABLE tmp;
CREATE TABLE tmp
(
  RXCUI VARCHAR2(8) NOT NULL,
  LAT VARCHAR2 (3) DEFAULT 'ENG' NOT NULL,
  TS VARCHAR2 (1),
  LUI VARCHAR2(8),
  STT VARCHAR2 (3),
  SUI VARCHAR2 (8),
  ISPREF VARCHAR2 (1),
  RXAUI VARCHAR2(8) NOT NULL,
  SAUI VARCHAR2 (50),
  SCUI VARCHAR2 (50),
  SDUI VARCHAR2 (50),
  SAB VARCHAR2 (20) NOT NULL,
  TTY VARCHAR2 (4) NOT NULL,
  CODE VARCHAR2 (50) NOT NULL,
  STR VARCHAR2 (3000) NOT NULL,
  SRL VARCHAR2 (10),
  SUPPRESS VARCHAR2 (1),
  CVF VARCHAR2(50)
)
;

*******************************************
*******************************************

tmp.ctl:
options (direct=true, errors=0)
load data
infile *
badfile 'tmp.bad'
discardfile 'tmp.dsc'
truncate
into table tmp
fields terminated by '|'
trailing nullcols
(
  RXCUI  char(8),
  LAT  char(3),
  TS  char(1),
  LUI  char(8),
  STT  char(3),
  SUI  char(8),
  ISPREF  char(1),
  RXAUI  char(8),
  SAUI  char(50),
  SCUI  char(50),
  SDUI  char(50),
  SAB  char(20),
  TTY  char(20),
  CODE  char(50),
  STR  char(3000),
  SRL  integer external,
  SUPPRESS  char(1),
  CVF  char(50)
)
BEGINDATA
347269|ENG|P||PF||Y|1536371|1536371|347269||RXNORM|SCD|347269|ALLERGENIC EXTRACT, DANDELION POLLEN 1 UNT / ALLERGENIC EXTRACT, FIREBUSH KOCHIA POLLEN 1 UNT / ALLERGENIC EXTRACT, GLODENROD POLLEN 1 UNT / ALLERGENIC EXTRACT- COCKLEBUR, MIXED XANTHIUM SPP. 1 UNT / ALLERGENIC EXTRACT- DOCK, SOUR OR SHEEP SORREL RUMEX ACETOSELLA 1 UNT / ALLERGENIC EXTRACT- DOCK, YELLOW RUMEX CRISPUS - CURLY DOCK 1 UNT / ALLERGENIC EXTRACT- HEMP, WATER 1 UNT / ALLERGENIC EXTRACT- LAMB'S QUARTERS CHENOPODIUM ALBUM 1 UNT / ALLERGENIC EXTRACT- MEXICAN TEA CHENOPODIUM AMBROSIOIDES 1 UNT / ALLERGENIC EXTRACT- MUGWORT, COMMON ARTEMISIA VULGARIS 1 UNT / ALLERGENIC EXTRACT- PIGWEED, ROUGH REDROOT AMARANTHUS RETROFLEXUS 1 UNT / ALLERGENIC EXTRACT- PLANTAIN, ENGLISH PLANTAGO LANCEOLATA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, SHORT AMBROSIA ELATIOR 1 UNT / ALLERGENIC EXTRACT- RAGWEED, TALL AMBROSIA TRIFIDA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, WESTERN AMBROSIA CORONOPIFOLIA 1 UNT / ALLERGENIC EXTRACT- RUSSIAN THISTLE SALSOLA KALI 1 UNT / ALLERGENIC EXTRACT- SAGEBRUSH, COMMON ARTEMISIA TRIDENTATA 1 UNT Injectable Solution||N||

*******************************************
*******************************************

E:\asktom>sqlldr john/john control='tmp.ctl'

SQL*Loader: Release 10.2.0.1.0 - Production on Fri Feb 9 09:44:48 200

Copyright (c) 1982, 2005, Oracle. All rights reserved.


Load completed - logical record count 1.

E:\asktom>type tmp.log

SQL*Loader: Release 10.2.0.1.0 - Production on Fri Feb 9 09:44:48 200

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Control File:  tmp.ctl
Data File:    tmp.ctl
Bad File:  tmp.bad
Discard File: tmp.dsc
(Allow all discards)

Number to load: ALL
Number to skip: 0
Errors allowed: 0
Continuation:  none specified
Path used:    Direct

Table TMP, loaded from every logical record.
Insert option in effect for this table: TRUNCATE
TRAILING NULLCOLS option in effect

  Column Name            Position  Len Term Encl Datatype
------------------------------ ---------- ----- ---- ---- -----------
RXCUI                    FIRST  8  |    CHARACTER
LAT                      NEXT  3  |    CHARACTER
TS                      NEXT  1  |    CHARACTER
LUI                      NEXT  8  |    CHARACTER
STT                      NEXT  3  |    CHARACTER
SUI                      NEXT  8  |    CHARACTER
ISPREF                    NEXT  1  |    CHARACTER
RXAUI                    NEXT  8  |    CHARACTER
SAUI                      NEXT  50  |    CHARACTER
SCUI                      NEXT  50  |    CHARACTER
SDUI                      NEXT  50  |    CHARACTER
SAB                      NEXT  20  |    CHARACTER
TTY                      NEXT  20  |    CHARACTER
CODE                      NEXT  50  |    CHARACTER
STR                      NEXT 3000  |    CHARACTER
SRL                      NEXT  *  |    CHARACTER
SUPPRESS                  NEXT  1  |    CHARACTER
CVF                      NEXT  50  |    CHARACTER


Table TMP:
1 Row successfully loaded.
0 Rows not loaded due to data errors.
0 Rows not loaded because all WHEN clauses were failed.
0 Rows not loaded because all fields were null.

Bind array size not used in direct path.
Column array rows :  5000
Stream buffer bytes: 256000
Read  buffer bytes: 1048576

Total logical records skipped:      0
Total logical records read:        1
Total logical records rejected:      0
Total logical records discarded:    0
Direct path multithreading optimization is disabled

Run began on Fri Feb 09 09:44:48 2007
Run ended on Fri Feb 09 09:44:49 2007

Elapsed time was:  00:00:00.66
CPU time was:      00:00:00.04

*******************************************
*******************************************

E:\asktom>flat john/john tmp >tmp.flat

tmp.flat:
347269|ENG|P||PF||Y|1536371|1536371|347269||RXNORM|SCD|347269|ALLERGENIC EXTRACT, DANDELION POLLEN 1 UNT / ALLERGENIC EXTRACT, FIREBUSH KOCHIA POLLEN 1 UNT / ALLERGENIC EXTRACT, GLODENROD POLLEN 1 UNT / ALLERGENIC EXTRACT- COCKLEBUR, MIXED XANTHIUM SPP. 1 UNT / ALLERGENIC EXTRACT- DOCK, SOUR OR SHEEP SORREL RUMEX ACETOSELLA 1 UNT / ALLERGENIC EXTRACT- DOCK, YELLOW RUMEX CRISPUS - CURLY DOCK 1 UNT / ALLERGENIC EXTRACT- HEMP, WATER 1 UNT / ALLERGENIC EXTRACT- LAMB'S QUARTERS CHENOPODIUM ALBUM 1 UNT / ALLERGENIC EXTRACT- MEXICAN TEA CHENOPODIUM AMBROSIOIDES 1 UNT / ALLERGENIC EXTRACT- MUGWORT, COMMON ARTEMISIA VULGARIS 1 UNT / ALLERGENIC EXTRACT- PIGWEED, ROUGH REDROOT AMARANTHUS RETROFLEXUS 1 UNT / ALLERGENIC EXTRACT- PLANTAIN, ENGLISH PLANTAGO LANCEOLATA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, SHORT AMBROSIA ELATIOR 1 UNT / ALLERGENIC EXTRACT- RAGWEED, TALL AMBROSIA TRIFIDA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, WESTERN AMBROSIA CORONOPIFOLIA 1 UNT / ALLERGENIC EXTRACT- RUSSIAN THISTLE SALS

*******************************************
*******************************************

john@ORCL> select str from tmp;

STR
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
------------------------------------------------------------
ALLERGENIC EXTRACT, DANDELION POLLEN 1 UNT / ALLERGENIC EXTRACT, FIREBUSH KOCHIA
POLLEN 1 UNT / ALLERGENIC EXTRACT, GLODENROD POLLEN 1 UNT / ALLERGENIC EXTRACT-
COCKLEBUR, MIXED XANTHIUM SPP. 1 UNT / ALLERGENIC EXTRACT- DOCK, SOUR OR SHEEP
SORREL RUMEX ACETOSELLA 1 UNT / ALLERGENIC EXTRACT- DOCK, YE
LLOW RUMEX CRISPUS - CURLY DOCK 1 UNT / ALLERGENIC EXTRACT- HEMP, WATER 1 UNT /
ALLERGENIC EXTRACT- LAMB'S QUARTERS CHENOPODIUM ALBUM 1 UNT / ALLERGENIC EXTRACT
- MEXICAN TEA CHENOPODIUM AMBROSIOIDES 1 UNT / ALLERGENIC EXTRACT- MUGWORT, COMM
ON ARTEMISIA VULGARIS 1 UNT / ALLERGENIC EXTRACT- PIGWEED, R
OUGH REDROOT AMARANTHUS RETROFLEXUS 1 UNT / ALLERGENIC EXTRACT- PLANTAIN, ENGLIS
H PLANTAGO LANCEOLATA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, SHORT AMBROSIA ELATIO
R 1 UNT / ALLERGENIC EXTRACT- RAGWEED, TALL AMBROSIA TRIFIDA 1 UNT / ALLERGENIC
EXTRACT- RAGWEED, WESTERN A

Followup   February 12, 2007 - 8am Central time zone:

$ ./flat / tmp
347269,ENG,P,,PF,,Y,1536371,1536371,347269,,RXNORM,SCD,347269,ALLERGENIC EXTRACT, DANDELION POLLEN 1 UNT / ALLERGENIC EXTRACT, FIREBUSH KOCHIA POLLEN 1 UNT / ALLERGENIC EXTRACT, GLODENROD POLLEN 1 UNT / ALLERGENIC EXTRACT- COCKLEBUR, MIXED XANTHIUM SPP. 1 UNT / ALLERGENIC EXTRACT- DOCK, SOUR OR SHEEP SORREL RUMEX ACETOSELLA 1 UNT / ALLERGENIC EXTRACT- DOCK, YELLOW RUMEX CRISPUS - CURLY DOCK 1 UNT / ALLERGENIC EXTRACT- HEMP, WATER 1 UNT / ALLERGENIC EXTRACT- LAMB'S QUARTERS CHENOPODIUM ALBUM 1 UNT / ALLERGENIC EXTRACT- MEXICAN TEA CHENOPODIUM AMBROSIOIDES 1 UNT / ALLERGENIC EXTRACT- MUGWORT, COMMON ARTEMISIA VULGARIS 1 UNT / ALLERGENIC EXTRACT- PIGWEED, ROUGH REDROOT AMARANTHUS RETROFLEXUS 1 UNT / ALLERGENIC EXTRACT- PLANTAIN, ENGLISH PLANTAGO LANCEOLATA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, SHORT AMBROSIA ELATIOR 1 UNT / ALLERGENIC EXTRACT- RAGWEED, TALL AMBROSIA TRIFIDA 1 UNT / ALLERGENIC EXTRACT- RAGWEED, WESTERN AMBROSIA CORONOPIFOLIA 1 UNT / ALLERGENIC EXTRACT- RUSSIAN THISTLE SALSOLA KALI 1 UNT / ALLERGENIC EXTRACT- SAGEBRUSH, COMMON ARTEMISIA TRIDENTATA 1 UNT Injectable Solution,,N,


I cannot reproduce.
5 stars Extract data from an XML view   February 13, 2007 - 10pm Central time zone
Reviewer: Rory from Philippines
Hi Tom,

I've got a view

SQL> desc itiread.cpu_xml
Name  Null?  Type
--------- -------- ------------------------
TABLE of XMLTYPE

This view contains XML data.

By setting the linesize, long, etc. I am able to extract the whole XML data (which is only one line of the view).
But my problem is, the spool file looks something like below. (This is only part of the spool file). My problem is, I met the MAX ALLOWABLE SIZE for LINESIZE in sqlplus, which makes my output below wrap, thereby giving me errors when I open the file (test.xml). The browser would give an error like 'end tag does not match the start tag'. This is because the whole XML is only one line. And it is about 200000 long. While the limit of LINESIZE is only 32k. Is there a workaround for this? Or if I can at least tell the select statement to spool to a newline when it sees an end tag like "</computer>", then at least the browser can read it.
Thanks.

<computers><computer id="14178708"><assettag>107459</assettag><hostname>ACVACTIDCPCTX07</hostname><model>/Server/INTEL Server/HP COMPAQ PROLIANT DL360 G4/</model><modelno>DL360 G4</modelno><serialno>USM506035W</serialno><location>/ITI/TIDC/1/1GBAY02/</location><startu>84</startu><assignment>In use</assignment><serverstatus>Production</serverstatus></computer><computer id="14178719"><assettag>107447</assettag><hostname>ACVACTIDCPDAT03</hostname><model>/Server/INTEL Server/HP COMPAQ PROLIANT DL380 G4/</model><modelno>DL380 G4</modelno><serialno>USX4450051</serialno><location>/ITI/TIDC/1/1GBAY03/</location><startu>27</startu><assignment>In use</assignment><serverstatus>Production</serverstatus></computer><computer id="14178734"><assettag>107462</assettag><hostname>ACVACTIDCPMAL01</hostname><model>/Server/INTEL Server/HP COMPAQ PROLIANT DL360 G4/</model><modelno>DL360 G4</modelno><serialno>USM50600N6</serialno><location>/ITI/TIDC/1/1GBAY01/</location><startu>61</startu><assignment>In use</assignment><serverstatus>Production</serverstatus></computer><computer id="14178747"><assettag>109347</assettag><hostname>CICTIDCSQL01</hostname><model>/Server/INTEL Server/HP COMPAQ PROLIANT DL380 G4/</model><modelno>DL380 G4</modelno><serialno>USE504A0FP</serialno><location>/ITI/TIDC/1/1EBAY26/</location><startu>47</startu><assignment>In use</assignment><serverstatus>Production</serverstatus></computer><computer id="14178782"><assettag>107204</assettag><hostname>ELWTTIDCWEB01</hostname><model>/Server/INTEL Server/HP COMPAQ PROLIANT DL360 G4/</model><modelno>DL360 G4</modelno><serialno>USM506033J</serialno><location>/ITI/TIDC/1/1EBAY07/</location><startu>51</startu><assignment>In use</assignment><serverstatus>Production</serverstatus></computer><computer id="14178793"><assettag>108845</assettag><hostname>FSHRPSILN01</hostname><model>/Server/INTEL Server/HP COMPAQ PROLIANT DL380 G3/</model><modelno>DL380 G3</modelno><serialno>D352LDN7J001</serialno><location>/ITI/LIDC/1/02-14-22/</loca
tion><



Followup   February 14, 2007 - 8am Central time zone:

write a real program using a real language instead of a simple command line scripting tool?

(you could write a plsql block of course that uses dbms_output - but be aware that in 9i and before the linesize for dbms_output is 255 which might be too small)
5 stars Extract XML data   February 14, 2007 - 3pm Central time zone
Reviewer: Rory from Philippines
Hi Tom,

Thanks for the prompt reply.
Is it possible for you to give me a sample code to generate my needed output using dbms_output. But of course with the necessary work around for the dbms_output limitations.
The one liner view is about 200000 long.

THanks a lot.

4 stars Extracting huge vol. of data into xml file using UTL_FILE   February 28, 2007 - 1pm Central time zone
Reviewer: Harsh 
Hi Tom ,

I am currently working on oracle 9 relese2
when i try to extarct huge volume of data i.e around 70000 records from the database into an xml file by executing the below mentioned code, it gives me the following error

expected symbol name is missing
DECLARE
*
ERROR at line 1:
ORA-20054: Invalid Operation
ORA-06512: at line 171.

can you please sort it out?

set serveroutput on
set define ~

spool c:\volunteerList2_harsh.xml

DECLARE
  CURSOR cVol IS
    SELECT * FROM volunteers WHERE EXISTS
       (SELECT 'X' FROM volunteer_details WHERE vdd_vdd_id = 167 and vl_pin = pin AND UPPER( 
attr_text ) = 'Y'); 
      
  cURSOR cVolDet( vId NUMBER ) IS
    SELECT data, description, domain
      FROM (SELECT DISTINCT DECODE( vdd.datatype, '1', TO_CHAR( vd.attr_number ), '2', 
vd.attr_text, 
      '3', REPLACE( TO_CHAR( vd.attr_date, 'YYYYMMDD-HH24MISS'), '-','T' ) ) data, vdd.description, 
vdd.domain, vdd.vdd_order
    FROM volunteer_detail_descriptions vdd, volunteer_details vd
    WHERE vd.vl_pin = vId
      AND vdd.vdd_id = vd.vdd_vdd_id
      AND UPPER( vdd.description ) NOT LIKE '%(OLD)%'
      AND UPPER( vdd.description ) NOT LIKE '%DO NOT USE%' )
    ORDER BY vdd_order;
  CURSOR cVolTrials( vId NUMBER ) IS
    SELECT trial_name, t_id, tt.description, start_date, end_date, payment, pay_method, 
payment_date
      FROM trial_types tt, trials t, volunteer_tests
      WHERE vl_pin = vId
        AND t_id = t_t_id
        AND tt_id = t.tt_tt_id;
  cLine VARCHAR2(32000);
  iIndent INTEGER := 0;
  temp VARCHAR2(32000);
  val VARCHAR2(32000);
  

  File1  utl_file.file_type;


  

  PROCEDURE xmlElement( xmlTag VARCHAR2, xmlData VARCHAR2 := '', xmlFunc VARCHAR2 := '', xmlAttr 
VARCHAR2 := '' ) IS
    cOut VARCHAR2(32000);
  BEGIN
   
    IF xmlFunc = 'C' THEN 
      iIndent := iIndent - 1;
    END IF;
    cOut := LPAD( '<', 2*iIndent+1 );
    IF xmlFunc = 'C' THEN 
      cOut := cOut || '/';
    END IF;
    cOut := cOut || xmlTag;
    IF xmlAttr IS NOT NULL AND xmlFunc <> 'C' THEN
      cOut := cOut || ' ' || xmlAttr;
    END IF;
    IF xmlFunc = 'O' THEN
      iIndent := iIndent + 1;
    ELSIF xmlFunc IS NULL AND xmlData IS NULL THEN
      cOut := cOut || ' /';
    END IF;
    cOut := cOut || '>';
    IF xmlData IS NOT NULL THEN
      cOut := cOut || REPLACE( REPLACE( REPLACE ( xmlData, '&', '&amp;' ), '<', '&lt;' ), '>', 
'&gt;' );
      IF xmlFunc IS NULL THEN
        cOut := cOut || '</' || xmlTag || '>';
      END IF;
    END IF;
    
    utl_file.put_line(File1, cOut,FALSE);




  END;



  FUNCTION tagName( name VARCHAR2 ) RETURN VARCHAR2 IS
    cOut VARCHAR2(4000);
  BEGIN
    cOut := TRANSLATE( name, '-_/():@&<>%$£^.\?,','                  ');
    cOut := INITCAP( cOut );
    cOut := REPLACE( cOut, ' ', '' );
    cOut := LOWER( SUBSTR( cOut, 1, 1 ) ) || SUBSTR( cOut, 2 );
    RETURN cOut;
  END;


  FUNCTION parse( data IN OUT VARCHAR2, delimiter VARCHAR2 := ' ' ) RETURN VARCHAR2 IS
    i INTEGER;
    output VARCHAR2(32000);
  BEGIN
    i := INSTR( data, delimiter );
    IF i > 0 THEN
      output := SUBSTR( data, 1, i - 1 );
      data := SUBSTR( data, i + LENGTH( delimiter ) );
    ELSE
      output := data;
      data := NULL;
    END IF;
    RETURN output;

  END;

  
BEGIN
    
   File1 := utl_file.fopen('ORALOAD','volunteerList22.xml', 'w');


  utl_file.put_line(File1,'<?xml version="1.0"?>',FALSE);

  xmlElement( 'volunteerList', '', 'O' );
  FOR rVol IN cVol LOOP
    xmlElement( 'volunteer', '', 'O', 'pin="' || TO_CHAR( rVol.pin ) || '"' );
    xmlElement( 'email', rVol.EMAIL );
    xmlElement( 'forename', rVol.FORENAME );
    xmlElement( 'middlenames', rVol.MIDDLENAMES );
    xmlElement( 'staffNo', TO_CHAR( rVol.STAFF_NO ) );
    xmlElement( 'surname', rVol.SURNAME );
    xmlElement( 'knownAs', rVol.KNOWNAS );
    xmlElement( 'addressLine1', rVol.ADDRESS_LINE1 );
    xmlElement( 'addressLine2', rVol.ADDRESS_LINE2 );
    xmlElement( 'addressLine3', rVol.ADDRESS_LINE3 );
    xmlElement( 'postCode', rVol.POSTCODE );
    xmlElement( 'telNo', rVol.TEL_NO );
    xmlElement( 'comments', rVol.COMMENTS );
    xmlElement( 'utPin', TO_CHAR( rVol.UT_PIN ) );
    xmlElement( 'department', TO_CHAR( rVol.DEPARTMENT ) );
    xmlElement( 'payGroup', rVol.PAY_GROUP );

    FOR rVolDet  IN cVolDet( rVol.pin ) LOOP
      IF rVolDet.domain IS NULL THEN
        xmlElement( tagName( rVolDet.description ), rVolDet.data );
      ELSE
        temp := TRANSLATE( rVolDet.data, '/\,', '   ' );
        WHILE temp IS NOT NULL LOOP
          val := parse( temp );
          IF val IS NOT NULL THEN
            BEGIN
              SELECT rv_low_value INTO val FROM cg_ref_codes WHERE rv_domain = rVolDet.domain AND 
rv_low_value = val;
              xmlElement( tagName( rVolDet.description ), val );
            EXCEPTION
              WHEN NO_DATA_FOUND THEN

                utl_file.put_line( File1, '<!--Illegal Value "' || val || '" for element "' || 
tagName( rVolDet.description ) || '"-->',FALSE);

            END;
          END IF;
        END LOOP;
    
 

      END IF;
    END LOOP;

    FOR rVolTrials IN cVolTrials( rVol.pin ) LOOP
      xmlElement( 'trial', '', 'O', 'id="' || TO_CHAR( rVolTrials.t_id ) || '"' );
      xmlElement( 'trialName', rVolTrials.trial_name );
      xmlElement( 'description', rVolTrials.description );
      xmlElement( 'startDate', REPLACE( TO_CHAR( rVolTrials.start_date, 'YYYYMMDD-HH24MISS' ), 
'-','T' ) );
      xmlElement( 'endDate', REPLACE( TO_CHAR( rVolTrials.end_date, 'YYYYMMDD-HH24MISS' ), '-','T' 
) );
      xmlElement( 'payment', TO_CHAR( rVolTrials.payment ) );
      xmlElement( 'payMethod', TO_CHAR( rVolTrials.pay_method ) );
      xmlElement( 'paymentDate', REPLACE( TO_CHAR( rVolTrials.payment_date, 'YYYYMMDD-HH24MISS' ), 
'-','T' ) );
      xmlElement( 'trial', '', 'C' );
    END LOOP;
    xmlElement( 'volunteer', '', 'C' );
  END LOOP;
  xmlElement( 'volunteerList', '', 'C' );
COMMIT;
utl_file.fclose(File1);        
    
EXCEPTION
    WHEN utl_file.invalid_path THEN
    RAISE_APPLICATION_ERROR (-20052, 'Invalid File Location');

    WHEN utl_file.invalid_operation THEN
    RAISE_APPLICATION_ERROR (-20054, 'Invalid Operation');
 
    WHEN others THEN
    RAISE_APPLICATION_ERROR (-20099, 'Unknown UTL_FILE Error');
 

  

END;


/

Spool Off;




if i use dbms_output BUFFER OVERFLOWS due to size limit. following is the code using dbms_output<code> set serveroutput on
set feedback off
set define ~
spool c:\volunteerListOriginal.xml
DECLARE
CURSOR cVol IS
  SELECT * FROM volunteers WHERE EXISTS
    ( SELECT 'X' FROM volunteer_details WHERE vdd_vdd_id = 167 and vl_pin = pin AND UPPER( attr_text ) = 'Y' ) ;
  -- AND ROWNUM <=20;
cURSOR cVolDet( vId NUMBER ) IS
  SELECT data, description, domain
    FROM (SELECT DISTINCT DECODE( vdd.datatype, '1', TO_CHAR( vd.attr_number ), '2', vd.attr_text,
    '3', REPLACE( TO_CHAR( vd.attr_date, 'YYYYMMDD-HH24MISS'), '-','T' ) ) data, vdd.description, vdd.domain, vdd.vdd_order
  FROM volunteer_detail_descriptions vdd, volunteer_details vd
  WHERE vd.vl_pin = vId
    AND vdd.vdd_id = vd.vdd_vdd_id
    AND UPPER( vdd.description ) NOT LIKE '%(OLD)%'
    AND UPPER( vdd.description ) NOT LIKE '%DO NOT USE%' )
  ORDER BY vdd_order;
CURSOR cVolTrials( vId NUMBER ) IS
  SELECT trial_name, t_id, tt.description, start_date, end_date, payment, pay_method, payment_date
    FROM trial_types tt, trials t, volunteer_tests
    WHERE vl_pin = vId
    AND t_id = t_t_id
    AND tt_id = t.tt_tt_id;
cLine VARCHAR2(32000);
iIndent INTEGER := 0;
temp VARCHAR2(32000);
val VARCHAR2(32000);

PROCEDURE xmlElement( xmlTag VARCHAR2, xmlData VARCHAR2 := '', xmlFunc VARCHAR2 := '', xmlAttr VARCHAR2 := '' ) IS
  cOut VARCHAR2(32000);
BEGIN
  IF xmlFunc = 'C' THEN
    iIndent := iIndent - 1;
  END IF;
  cOut := LPAD( '<', 2*iIndent+1 );
  IF xmlFunc = 'C' THEN
    cOut := cOut || '/';
  END IF;
  cOut := cOu

3 stars spool clob on to a file   March 7, 2007 - 3am Central time zone
Reviewer: Umesh Kasturi from Bangalore , India
Tom

I want to display a very large data in a single row.
I am concatenating all the values of a column called col1 and want to spool to a file . But when I try this it puts values in a separate lines
How do I over come that?

Thanks in advance



set long 5000000000
set termout off


alter table emp add col1 varchar2(4000);

update emp set col1 = rpad('*',3999,'*');

CREATE OR REPLACE FUNCTION lg_concatenate_list (p_cursor IN SYS_REFCURSOR)
RETURN CLOB
IS
l_clob CLOB ;
l_temp  VARCHAR2(32767);

BEGIN
dbms_lob.createTemporary( l_clob, true );
dbms_lob.open( l_clob, dbms_lob.lob_readwrite );

LOOP
  FETCH p_cursor  INTO l_temp;
  EXIT WHEN p_cursor%NOTFOUND;
  dbms_lob.writeappend (l_clob, length(l_temp), l_temp );
END LOOP;
CLOSE p_cursor;
  RETURN l_clob;
END;
/


connect scott/tiger

spool d:\temp\concat.csv

select lg_concatenate_list(cursor (select col1 from emp) )l from dual;

spool off

Followup   March 7, 2007 - 10am Central time zone:

SQL> set linesize N
3 stars Adds carriage return Separate lines   March 9, 2007 - 5am Central time zone
Reviewer: umesh_kasturi from Bangalore India
********************************************************************************
********************************************************************************
********************************************************************************
I did try by having set lines 200
Here is a copy paste of my output. Actually I see that it appends a Carraige return at the end. I did try to remove by putting a "if ... end if" logic but it still persists
How do I overcome that and print it in one singl line when I spool the same

Followup   March 9, 2007 - 11am Central time zone:

sorry - not making sense to me
4 stars extracting data to a flat file   March 9, 2007 - 3pm Central time zone
Reviewer: Tarang from Lexington, MA
This is what I use to extract data to a flat file.
Hope that helps.

-----------------------------
create or replace procedure datapull(isql in varchar2, ifilename in out varchar2, orowsexported out number)
is
  t_sqlstatement varchar2(4000);
  t_sourcecursorid number;
  t_columncount integer :=0;
  t_columntable dbms_sql.desc_tab;
  rc number;
  --
  t_outfileid utl_file.file_type;
  t_outfilename varchar2(100);
  t_job_id number;
  --
  t_runid number :=0;
  t_errormsg varchar2(4000);
  t_exportedrows number := 0;
  --
  x number := 0;
  tmp varchar2(32767);
  data varchar2(32767);
  clob_data clob;
  --
begin
  begin
    t_sqlstatement := replace(replace(trim(isql),chr(13),' '),chr(10),' ');
    t_sourcecursorid := dbms_sql.open_cursor;
    dbms_sql.parse(t_sourcecursorid,t_sqlstatement,dbms_sql.native);
    dbms_sql.describe_columns(t_sourcecursorid,t_columncount,t_columntable);
    exception
        when others then
          rollback;
          t_errormsg := 'error parsing source cursor:'|| to_char(sqlcode) || ':' || sqlerrm;
          dbms_output.put_line(t_errormsg);
          return;
  end;
  --
  x := nvl(length(trim(ifilename)),0);
  if x > 0 then
    t_outfilename:= trim(ifilename)||'.txt';
  else
    t_errormsg := 'error outputfilename not specified:'|| to_char(sqlcode) || ':' || sqlerrm;
    dbms_output.put_line(t_errormsg);
    return;
  end if;
  --
  begin
    t_outfileid := utl_file.fopen('data_dir',t_outfilename,'w',32767);
    dbms_output.put_line('out file open: ' || t_outfilename);
    --
    for x in 1 .. t_columncount
    loop
        dbms_output.put_line(t_columntable(x).col_name || ' is type:' || t_columntable(x).col_type);
        if t_columntable(x).col_type = 112 then -- is clob
          dbms_sql.define_column(t_sourcecursorid,x,clob_data);
        else
          dbms_sql.define_column(t_sourcecursorid,x,data,10000);
        end if;
        if x = 1 then
          tmp := upper(t_columntable(x).col_name);
        else
          tmp := chr(9) || upper(t_columntable(x).col_name);
        end if;
        utl_file.put(t_outfileid,tmp);
    end loop;
    --
    rc := dbms_sql.execute(t_sourcecursorid);
    --
    loop
        exit when dbms_sql.fetch_rows(t_sourcecursorid) = 0;
        utl_file.put_line(t_outfileid,null);
        for x in 1 .. t_columncount
        loop
          if t_columntable(x).col_type = 112 then -- is clob
            if dbms_lob.getlength(clob_data) > 32700 then
                t_errormsg := 'error clob too big, length: ' || dbms_lob.getlength(clob_data);
                dbms_output.put_line(t_errormsg);
                return;
            end if;
            dbms_sql.column_value(t_sourcecursorid,x,clob_data);
            data := dbms_lob.substr(clob_data,32700,1);
          else
            dbms_sql.column_value(t_sourcecursorid,x,data);
          end if;
          --
          data := trim(data);
          if x = 1 then
            tmp := data;
          else
            tmp := chr(9) || data;
          end if;
          utl_file.put(t_outfileid,tmp);
        end loop;
        t_exportedrows := t_exportedrows + 1;
    end loop;
    --
      exception
        when others then
          rollback;
          t_errormsg := 'error exporting data:'|| to_char(sqlcode) || ':' || sqlerrm;
          dbms_output.put_line(t_errormsg);
          return;
  end;
  --
  dbms_sql.close_cursor(t_sourcecursorid);
  utl_file.fclose(t_outfileid);
  dbms_output.put_line('rows exported:' || t_exportedrows);
  t_errormsg := 'data pull job completed';
  ifilename := t_outfilename;
  orowsexported := t_exportedrows;
exception
  when others then
    rollback;
    t_errormsg := 'error :'|| to_char(sqlcode) || ':' || sqlerrm;
    dbms_output.put_line(t_errormsg);
    raise_application_error(-20001,'error datapull: '||to_char(sqlcode)||':'||sqlerrm);
end datapull;
/


5 stars Special Characters   April 13, 2007 - 6am Central time zone
Reviewer: A reader 
Hi Tom,
I'm using this dump_csv function for extracting the data. How do we delimit the special characters (like ') in the below sql query. I have a couple of queries for which I need to extract data in flat file. And the data is huge. Please can you advice on this.

create or replace procedure test_dump_csv
as
  l_rows number;
begin
  l_rows := dump_csv( 'select *
                  from all_objects
                  where owner='CTXSYS',
                ',', 'd:\temp\output', 'test.csv' );
end;
/
Warning: Procedure created with compilation errors.

SQL> show errors
Errors for PROCEDURE TEST_DUMP_CSV:

LINE/COL ERROR
-------- -----------------------------------------------------------------
8/41  PLS-00103: Encountered the symbol "CTXSYS" when expecting one of
      the following:
      . ( ) , * @ % & | = - + < / > at in is mod not range rem =>
      .. <an exponent (**)> <> or != or ~= >= <= <> and or like as
      between from using ||
      The symbol ". was inserted before "CTXSYS" to continue.

Many Thanks

Followup   April 13, 2007 - 2pm Central time zone:

to get a ' in a character string literal, you use ''

ops$tkyte%ORA9IR2> select 'how''s this going to work' from dual;

'HOW''STHISGOINGTOWORK'
------------------------
how's this going to work



or, new in 10g, q'| .... |'

ps$tkyte%ORA10GR2> select q'|how's this going to work|' from dual;

Q'|HOW'STHISGOINGTOWORK|
------------------------
how's this going to work


5 stars Or use chr(39) ?   April 16, 2007 - 6am Central time zone
Reviewer: Paul from Manchester, England
Using chr sometimes makes things easier to read, like when you unstring individual words from a comma separated string. Anyway, on a similar theme for those of us without 10G:
1 select 'how'
2 ||
3 chr(39)
4 ||
5 's this going to work'
6* from dual
PS@dev> /

'HOW'||CHR(39)||'STHISGO
------------------------
how's this going to work

1 row selected.

PS@dev>

4 stars   April 16, 2007 - 12pm Central time zone
Reviewer: A reader 
Many thanks Tom
Your inputs are always very useful.

5 stars   May 1, 2007 - 3pm Central time zone
Reviewer: Alexander the ok 
Tom,

See if this makes sense to you. I want to allow developers to use your cool dump_csv function. I granted them read/write to the directory object.

The dir permissions look like this:

SQL> !ls -la /usr/apps/corpis/planit/ftp/utl_files
total 6120
drwxrwxrwx   2 n0002220 users           256 May  1 14:09 .
drwxrwxr-x   4 n0009888 users          4096 May  1 12:50 ..
-rw-r--r--   1 oracle   dba             923 May  1 13:53 test.dat
-rw-r-----   1 oracle   dba         3121285 May  1 14:09 w_test.out


What I don't understand, when I run dump_csv (as the oracle software account), it creates the file with world read, when they do, it does not. If utl_file is writing to the directory using the oracle software account why isn't it the same for both files?

Followup   May 1, 2007 - 4pm Central time zone:

when you log into oracle, do you use sqlnet or do you use a direct connection and do they do it differently?
5 stars   May 1, 2007 - 4pm Central time zone
Reviewer: Alexander the ok 
I use sqlplus oracle/pw

they either connect from their pc or sqlplus user/pwd@db

Followup   May 1, 2007 - 9pm Central time zone:

ok, their umask is inherited from the listeners environment.

your umask is inherited from your environment..

that is, their dedicated server, the process that makes the file, is spawned by the listener

your dedicated server is spawned by you, your environment.

I'll bet your default umasks are different.
5 stars   May 2, 2007 - 9am Central time zone
Reviewer: Alexander 
If that's the case, what can I do about it? Do I have to get a SA to change theirs? I typed in umask and got back 022. Don't know what that means though.

Followup   May 2, 2007 - 9am Central time zone:

look at the umask of the thing that starts the listener.

$ man umask

will be useful to understand what it is.


You will likely find the umask of the environment that creates the listener is 0137:

[tkyte@desktop 10gNew]$ umask
0137
[tkyte@desktop 10gNew]$ umask -S
u=rw,g=r,o=



Yours:
[tkyte@desktop 10gNew]$ umask 022
[tkyte@desktop 10gNew]$ umask -S
u=rwx,g=rx,o=rx


gave read to all.
5 stars   May 2, 2007 - 11am Central time zone
Reviewer: Alexander 
I don't think I understand quite what you are saying. Running umask 022 will give everyone the ability to read files created by oracle? I ran it but they still can't get at their files.

Or I need to give the listener the 022? Thanks for the help, I struggle with Unix....

Followup   May 2, 2007 - 5pm Central time zone:

the environment the listener is running it - its umask is "X"

that umask of X, it is giving the file the rw r - you are seeing.

You need to make the environment of the listener, the thing that starts the listener have a umask of Y, where Y has the file permissions you desire.
5 stars   May 2, 2007 - 5pm Central time zone
Reviewer: Alexander 
The part that does not compute to me is the "thing that starts the listener". I don't know how to identify this, or change it's umask. Is it a process? A user? tia

Followup   May 2, 2007 - 6pm Central time zone:

do you have a system administrator with you that does know your OS and how it works?

yes, this is the "thing that starts the listener", the "thing logged in and it's environment when you start the listener"


5 stars   May 2, 2007 - 7pm Central time zone
Reviewer: Alexander 
We use the oracle OS account to do everything, including starting/stopping listeners if needed. That has 022 so I'm still confused but I'll try and get a SAs input. I have a feeling they're not going to know anything about oracle listeners though.

5 stars   May 15, 2007 - 8am Central time zone
Reviewer: Wilson from Zambia
I tried to run this script in OracleXE and I get an error, kindly assist. I have the utl_file_dir in the INIT.ora and shows that it is set:
SQL> ed
Wrote file afiedt.buf

1 create or replace
2  function dump_csv( p_query    in varchar2,
3                p_separator in varchar2 default ',',
4                p_dir    in varchar2 ,
5                p_filename in varchar2 )
6    return number
7    is
8      l_output    utl_file.file_type;
9      l_theCursor  integer default dbms_sql.open_cursor;
10    l_columnValue  varchar2(2000);
11    l_status    integer;
12      l_colCnt    number default 0;
13      l_separator  varchar2(10) default '';
14      l_cnt      number default 0;
15  begin
16      l_output := utl_file.fopen( p_dir, p_filename, 'w' );
17      dbms_sql.parse( l_theCursor, p_query,
18                              dbms_sql.native );
19      for i in 1 .. 255 loop
20        begin
21          dbms_sql.define_column( l_theCursor, i,
22                          l_columnValue, 2000 );
23          l_colCnt := i;
24        exception
25          when others then
26              if ( sqlcode = -1007 ) then exit;
27              else
28                raise;
29              end if;
30        end;
31      end loop;
32      dbms_sql.define_column( l_theCursor, 1,
33                      l_columnValue, 2000 );
34      l_status := dbms_sql.execute(l_theCursor);
35      loop
36        exit when ( dbms_sql.fetch_rows(l_theCursor) <= 0 );
37        l_separator := '';
38        for i in 1 .. l_colCnt loop
39          dbms_sql.column_value( l_theCursor, i,
40                          l_columnValue );
41          utl_file.put( l_output,
42                    l_separator || l_columnValue );
43          l_separator := p_separator;
44        end loop;
45        utl_file.new_line( l_output );
46        l_cnt := l_cnt+1;
47      end loop;
48      dbms_sql.close_cursor(l_theCursor);
49      utl_file.fclose( l_output );
50      return l_cnt;
51*  end dump_csv;
52 /

Warning: Function created with compilation errors.

SQL> /

Warning: Function created with compilation errors.

SQL> show error
Errors for FUNCTION DUMP_CSV:

LINE/COL ERROR
-------- -----------------------------------------------------------------
7/25  PLS-00201: identifier 'UTL_FILE' must be declared
7/25  PL/SQL: Item ignored
15/8  PLS-00320: the declaration of the type of this expression is
      incomplete or malformed

15/8  PL/SQL: Statement ignored
40/16  PL/SQL: Statement ignored
40/30  PLS-00320: the declaration of the type of this expression is
      incomplete or malformed

44/12  PL/SQL: Statement ignored
44/31  PLS-00320: the declaration of the type of this expression is
      incomplete or malformed

48/8  PL/SQL: Statement ignored
48/25  PLS-00320: the declaration of the type of this expression is
      incomplete or malformed

Followup   May 15, 2007 - 8pm Central time zone:

grant yourself execute on utl_file.

it is not granted by default
4 stars   May 17, 2007 - 10am Central time zone
Reviewer: A reader from UK
Tom,
Is it safe to use the 'create directory' instead of setting the utl_file_dir. I mean if we grant read, write to the user for which this extract is aplicable.
Thank You

Followup   May 17, 2007 - 11am Central time zone:

it is safer to use a directory, more granular security is available.

you should not use utl_file_dir in current releases.
4 stars no column headers   July 20, 2007 - 9am Central time zone
Reviewer: A reader 
Tom,
I'm trying to dump the data in csv format by using you dump_csv function. The only problem is that 
I'm not able to get the column headers in the output. Your assistance is required. 
Thanks in advance.


Followup   July 20, 2007 - 4pm Central time zone:

ctl-f (search) for headings on this page.
4 stars no column headers   July 20, 2007 - 10am Central time zone
Reviewer: A reader 
Tom,
I'm trying to dump the data in csv format by using you dump_csv function. The only problem is that 
I'm not able to get the column headers in the output. For Eg:
select owner, object_name, object_type from user_objects ;
How do i get the headers OWNER OBJECT_NAME OBJECT_TYPE
Your assistance is required. 
Thanks in advance.


4 stars no column headers   August 3, 2007 - 11am Central time zone
Reviewer: A reader 
Tom,
I managed to get it temporary through a small change. I just added a line before the select as 
select 'OWNER' Owner, 'OBJECT_NAME' Object_name, 'OBJECT_TYPE' Object_type from dual
 union
select ....
So, that I get the first line as the 
Owner,Object_name,Object_type...
Thanks Tom


5 stars unload to csv file   August 28, 2007 - 7am Central time zone
Reviewer: sunitha from india
hi
i am new to oracle
i have used the function which is in this review

create or replace function  dump_csv( p_query     in varchar2,
                                      p_separator in varchar2
                                                    default ',',
                                      p_dir       in varchar2 ,
                                      p_filename  in varchar2 )
return number
AUTHID CURRENT_USER
is
    l_output        utl_file.file_type;
    l_theCursor     integer default dbms_sql.open_cursor;
    l_columnValue   varchar2(2000);
    l_status        integer;
    l_colCnt        number default 0;
    l_separator     varchar2(10) default '';
    l_cnt           number default 0;
begin
    l_output := utl_file.fopen( p_dir, p_filename, 'w' );

    dbms_sql.parse(  l_theCursor,  p_query, dbms_sql.native );

    for i in 1 .. 255 loop
        begin
            dbms_sql.define_column( l_theCursor, i,
                                    l_columnValue, 2000 );
            l_colCnt := i;
        exception
            when others then
                if ( sqlcode = -1007 ) then exit;
                else
                    raise;
                end if;
        end;
    end loop;

    dbms_sql.define_column( l_theCursor, 1, l_columnValue,
                            2000 );

    l_status := dbms_sql.execute(l_theCursor);

    loop
        exit when ( dbms_sql.fetch_rows(l_theCursor) <= 0 );
        l_separator := '';
        for i in 1 .. l_colCnt loop
            dbms_sql.column_value( l_theCursor, i,
                                   l_columnValue );
            utl_file.put( l_output, l_separator ||
                                    l_columnValue );
            l_separator := p_separator;
        end loop;
        utl_file.new_line( l_output );
        l_cnt := l_cnt+1;
    end loop;
    dbms_sql.close_cursor(l_theCursor);

    utl_file.fclose( l_output );
    return l_cnt;
end dump_csv;
/


Function Created

create or replace procedure test_dump_csv
as
    l_rows  number;
begin
    l_rows := dump_csv( 'select *
                           from sysadm.centre',
                        ',', c:\test', 'test1.dat' );
end;
/
Procedure Created



But the test folder in C drive is empty(test1.dat not created)


Followup   September 4, 2007 - 12pm Central time zone:

you do understand that plsql runs in the server, on the server and the file would be created on the DATABASE SERVER right?
5 stars TEXT_IO   September 5, 2007 - 6am Central time zone
Reviewer: suni from India
Hi Tom,
I made the function dump_csv to procedure, created directory utl_file_dir as 'c:\temp\utl_file', 
its working fine,
In the review you said that we have to use TEXT_IO to save on workstation.
so i replaced utl_file with text_io,
but i got the warning: Warning: Procedure created with compilation errors. 
the data in error lines
 8      l_output        TEXT_IO.FILE_TYPE;
16      l_output := TEXT_IO.FOPEN( p_dir, p_filename, 'w' );
 40              TEXT_IO.PUT( l_output, l_separator ||  
 41                                      '"'||l_columnValue||'"' );
 44          TEXT_IO.NEW_LINE( l_output );
48      TEXT_IO.FCLOSE( l_output );
SQL> SHOW ERRORS
Errors for PROCEDURE DUMP_CSV:

LINE/COL ERROR
-------- -----------------------------------------------------------------
8/21     PL/SQL: Item ignored
8/21     PLS-00201: identifier 'TEXT_IO.FILE_TYPE' must be declared
16/5     PL/SQL: Statement ignored
16/5     PLS-00320: the declaration of the type of this expression is
         incomplete or malformed

40/13    PL/SQL: Statement ignored
40/26    PLS-00320: the declaration of the type of this expression is
         incomplete or malformed

44/9     PL/SQL: Statement ignored

LINE/COL ERROR
-------- -----------------------------------------------------------------
44/27    PLS-00320: the declaration of the type of this expression is
         incomplete or malformed

48/5     PL/SQL: Statement ignored
48/21    PLS-00320: the declaration of the type of this expression is
         incomplete or malformed


Followup   September 5, 2007 - 4pm Central time zone:

text_io is for oracle forms


you cannot use plsql running IN THE SERVER to write to your disk

you can use plsql running in a forms server to write to the machine the form is running on.


you have to have a client program running on a client to write to that client (otherwise you have a "virus" like thing!)
4 stars Problem with arabic data when using dump_csv   September 28, 2007 - 8am Central time zone
Reviewer: mohamad shehadeh from Lebanon
Dear sir,
i got unreadable text while using dump_csv with arabic data
Can you please help me to use with arabic data


Followup   September 28, 2007 - 5pm Central time zone:

back up and just write a tiny file with utl_file - and if you get "garbage", you have the perfect test case for support.
5 stars csv   January 22, 2008 - 5pm Central time zone
Reviewer: A reader 
Tom:

Is this  creating a file on the server. I could not find it under /tmp?

Can I have it create one on the client machine?


create or replace procedure test_dump_csv
as
    l_rows  number;
begin
    l_rows := dump_csv( 'select * 
                           from all_users 
                           where rownum < 25', 
                        ',', '/tmp', 'test.dat' );
end;
/


Followup   January 22, 2008 - 6pm Central time zone:

http://asktom.oracle.com/tkyte/flat/index.html


sqlplus and SPOOL works for a client dump


utl_file can ONLY write to the server, utl_file is running in plsql,plsql runs on the server.

if utl_file could write to YOUR file system - it would be deemed (rightly so) a virus. Something running on the database cannot write to your filesystem (unless of course you mounted your filesystem read/write to the database server...)


5 stars csv   January 22, 2008 - 9pm Central time zone
Reviewer: A reader 
TOm:

I was testing with a simple text file and excel read it fine

A,100,200,300
B,200,400,500

Can't  I just create any query and stick ||','|| between the columns, save it to my client machine 
and then open it in Excel

like

spool emp.txt
select empid||','||last_name||','||first_name from emp;



Followup   January 23, 2008 - 7am Central time zone:

of course you can.

see the *very first link* in the original answer - it has a script to do just that.
5 stars spool inside anonymous block   March 1, 2008 - 4am Central time zone
Reviewer: Vishal from india
Hi Tom,
  I Have a script in which based on some cursor values it is creating some text file.due to some restriction i can't use "UTL_FILE" so i am spooling all the files.but spool is sql statment so it's not working in this block.
here is sql script->
Declare
  posicio number;
  nivell number:=0;
  name varchar2(30);
  name2 varchar2(30);
CURSOR crea IS
  SELECT * FROM STRUCTURE
    WHERE (parent_part IS NULL)
      and (product_line = '30'
      or product_line = '7T'
      or product_line = 'LF')
      and (substr(component_part,1,1) <> 'U')
      and (length(component_part) = 6
      or substr(component_part,1,2) = 'R-'
      or substr(component_part,1,7) = 'REFURB-'
      or substr(component_part,7,4) = '-000'
      or product_line <> '30'
      or
        ((length(component_part) = 11) and
          component_part = 'Q6702-64001' or
          component_part = 'Q6703-64001')
      or
        ((substr(component_part,7,2) = '#A' or
          substr(component_part,7,2) = '' or
          substr(component_part,7,2) = '#M' or
          substr(component_part,7,2) = '#U' or
          substr(component_part,7,2) = '#2' or
          substr(component_part,7,2) = '#4') and
          (substr(component_part,1,6) = 'C7769E' or
          substr(component_part,1,6) = 'C7769F' or
          substr(component_part,1,6) = 'C7769G' or
          substr(component_part,1,6) = 'C7770E' or
          substr(component_part,1,6) = 'C7770F' or
          substr(component_part,1,6) = 'C7770G' or
          substr(component_part,1,6) = 'C7790D' or
          substr(component_part,1,6) = 'C7790E' or
          substr(component_part,1,6) = 'C7790F' or
          substr(component_part,1,6) = 'C7791C' or
          substr(component_part,1,6) = 'C7791D' or
          substr(component_part,1,6) = 'C7791E' or
            substr(component_part,1,6) = 'C7791K' or
          substr(component_part,1,6) = 'C7796C' or
          substr(component_part,1,6) = 'C7796D' or
          substr(component_part,1,6) = 'C7796E' or
          substr(component_part,1,6) = 'CG710A' or
          substr(component_part,1,6) = 'Q1251E' or
          substr(component_part,1,6) = 'Q1253E' or
          substr(component_part,1,6) = 'Q1271A' or
          substr(component_part,1,6) = 'Q1272A' or
          substr(component_part,1,6) = 'Q1273A' or
          substr(component_part,1,6) = 'Q1274A' or
          substr(component_part,1,6) = 'Q1276A' or
          substr(component_part,1,6) = 'Q1277A' or
          substr(component_part,1,6) = 'Q5669A' or
          substr(component_part,1,6) = 'Q5669B' or
          substr(component_part,1,6) = 'Q5670A' or
          substr(component_part,1,6) = 'Q5671A' or
          substr(component_part,1,6) = 'Q5672A' or
          substr(component_part,1,6) = 'Q5677A' or
          substr(component_part,1,6) = 'Q5677B' or
          substr(component_part,1,6) = 'Q6648A' or
          substr(component_part,1,6) = 'Q6651A' or
          substr(component_part,1,6) = 'Q6652A' or
          substr(component_part,1,6) = 'Q6653A' or
          substr(component_part,1,6) = 'Q6654A' or
          substr(component_part,1,6) = 'Q6655A' or
          substr(component_part,1,6) = 'Q6656A' or
          substr(component_part,1,6) = 'Q6656B' or
          substr(component_part,1,6) = 'Q6656C' or
          substr(component_part,1,6) = 'Q6659A' or
          substr(component_part,1,6) = 'Q6660A' or
          substr(component_part,1,6) = 'Q6665A' or
          substr(component_part,1,6) = 'Q6666A' or
          substr(component_part,1,6) = 'Q6667A' or
          substr(component_part,1,6) = 'Q6668A' or
          substr(component_part,1,6) = 'Q6668B' or
          substr(component_part,1,6) = 'Q6668C' or
          substr(component_part,1,6) = 'Q6668D' or
          substr(component_part,1,6) = 'Q6668E' or
          substr(component_part,1,6) = 'Q6668F' or
          substr(component_part,1,6) = 'Q6670A' or
          substr(component_part,1,6) = 'Q6670B' or
          substr(component_part,1,6) = 'Q6671A' or
          substr(component_part,1,6) = 'Q6673A' or
          substr(component_part,1,6) = 'Q6673B' or
          substr(component_part,1,6) = 'Q6675A' or
          substr(component_part,1,6) = 'Q6675B' or
          substr(component_part,1,6) = 'Q6677A' or
          substr(component_part,1,6) = 'Q6683A' or
          substr(component_part,1,6) = 'Q6684A' or
          substr(component_part,1,6) = 'Q6685A' or
          substr(component_part,1,6) = 'Q6686A' or
          substr(component_part,1,6) = 'Q6687A' or
          substr(component_part,1,6) = 'Q6688A' or
          substr(component_part,1,6) = 'Q6693A' or
          substr(component_part,1,6) = 'Q6694A' or
          substr(component_part,1,6) = 'Q6711A' or
          substr(component_part,1,6) = 'Q6712A' or
          substr(component_part,1,6) = 'Q6713A'))
      or
        ((substr(component_part,7,2) = '-0' or
          substr(component_part,7,2) = '-1' or
          substr(component_part,7,2) = '-2') and
        (substr(component_part,1,6) = 'C7769E' or
          substr(component_part,1,6) = 'C7769F' or
          substr(component_part,1,6) = 'C7770E' or
          substr(component_part,1,6) = 'C7770F' or
          substr(component_part,1,6) = 'C7790D' or
          substr(component_part,1,6) = 'C7791C' or
          substr(component_part,1,6) = 'C7791D' or
          substr(component_part,1,6) = 'C7791K' or
          substr(component_part,1,6) = 'C7796C' or
          substr(component_part,1,6) = 'C7796D' or
          substr(component_part,1,6) = 'C7796E' or
          substr(component_part,1,6) = 'Q1271A' or
          substr(component_part,1,6) = 'Q1273A' or
          substr(component_part,1,6) = 'Q1277A' or
          substr(component_part,1,6) = 'Q5669A' or
          substr(component_part,1,6) = 'Q6655A' or
          substr(component_part,1,6) = 'Q6651A' or
          substr(component_part,1,6) = 'Q6652A' or
          substr(component_part,1,6) = 'Q6653A' or
          substr(component_part,1,6) = 'Q6654A' or
          substr(component_part,1,6) = 'Q6656A' or
          substr(component_part,1,6) = 'Q6656B' or
          substr(component_part,1,6) = 'Q6659A' or
          substr(component_part,1,6) = 'Q6675A' or
          substr(component_part,1,6) = 'Q6677A' or
          substr(component_part,1,6) = 'Q6683A' or
          substr(component_part,1,6) = 'Q6685A' or
          substr(component_part,1,6) = 'Q6687A' or
          substr(component_part,1,6) = 'Q6711A' or
          substr(component_part,1,6) = 'Q6712A')))
    ORDER BY component_part;
CURSOR crea2 IS
  SELECT distinct rtrim(a.component_part) component_part2,
        'zz' product_line2,
        b.description,
        b.qty_per,
        b.document_resp,
        b.phantom_flag,
        b.part_class,
        b.explode_code
  FROM GAMMA_INSERT a, STRUCTURE b
  WHERE rtrim(a.component_part) = b.component_part
    ORDER BY rtrim(a.component_part);
BEGIN
FOR cr IN crea LOOP
  name:=cr.component_part;
  loop
          posicio:=instr(name,'/');
          if posicio <> 0 then
        name2:=substr(name,1,posicio-1)||'-'||
            substr(name, posicio+1, LENGTH(name));
          name:=name2;
    else
        exit;
    end if;
  end loop;
*--Here i want to create a txt file based on cursor--*
spool c:\'||cr.product_line||name||'.txt';---->(error giving)
dbms_output.put_line('Part List for  '||cr.component_part||'  '||cr.description);
dbms_output.put_line(' ');
--dbms_output.put_line('BCD BOM snapshot date (Barcelona time) :  '||    to_char(sysdate,'YYYY/MM/DD HH24:MI'));
dbms_output.put_line(' ');
dbms_output.put_line('Level  Part-number  Description            Quantity  DR    Revision');
dbms_output.put_line('-----  -----------  -------------  --------  --    --------');
dbms_output.put_line(' ');
dbms_output.put_line(lpad(to_char(nivell),2,'0')||'  '||cr.component_part||'  '||cr.description||'  '||cr.qty_per||'  '||cr.document_resp);
END LOOP;
End;

or do you have any other idea we can implement this.

Vishal




Followup   April 3, 2008 - 7pm Central time zone:



(do you know about "in" where substr(a,1,2) in ( 'aa', 'bb', .... ))

you cannot of course interact with sqlplus from plsql since plsql is submitted to the server and runs on the server.

You'll really want to write a program in order to do your custom data extraction, sqlplus is a rather simple command line tool to talk to Oracle, it really isn't for programming.
5 stars how generate a text file   April 3, 2008 - 3am Central time zone
Reviewer: hArOld kAwAii from PHILIPPINES
hi tom,
 i am new in oracle 10g.. i would like to ask the script from top to bottom on how to generate this 
sample text to a text file..

can i view the generated text?

heres the sentence line by line

Your application status is : DONE
VECO|
N
09164324030
N,Y
~


Followup   April 3, 2008 - 7pm Central time zone:

what language are you going to be programming in is the very first question that pops into my mind.

the second would be "why would you need to be told how to produce a text file in that language" - because I would presume you program in it.


5 stars how to generate a text file   April 3, 2008 - 3am Central time zone
Reviewer: hArOld kAwAii from PHILIPPINES
hi tom,
 i am new in oracle 10g.. i would like to ask the script from top to bottom on how to generate this 

sample text to a text file..

can i view the generated text?

heres the sentence/words line by line

Your application status is : DONE
VECO|
N
09164324030
N,Y
~

sori for the wrong grammar.

thanks in advance.

god bless


5 stars re: how to generate a text file   April 3, 2008 - 9pm Central time zone
Reviewer: hArOld kAwAii from PHILIPPINES
i just dont know how to use oracle....

i am a beginner/novice in oracle then my boss wants me to make a job/scheduler that would generate 
a text file and saved in a particular path outside oracle OS probably in C:\TXTCONNECT... 

thanks,

sorry if my question is not direct as you want it to be..


Followup   April 4, 2008 - 9am Central time zone:

forget about oracle for a second, on this machine you are going to create this "job scheduler" - WHAT PROGRAMMING LANGUAGE are you planning on coding in.

eg: what *tool* are you going to use to program all of this.
5 stars how to generate a text file   April 3, 2008 - 9pm Central time zone
Reviewer: hArOld kAwAii from PHILIPPINES
 
by the way, i am using PLSQL


thanks,


5 stars output file   April 3, 2008 - 10pm Central time zone
Reviewer: hArOld kAwAii from PHILIPPINES
hi tom,

i would just rephrase my question,

1. is oracle plsql capable of saving a .txt file to the Desktop?

sorry for the past questions i've posted.

thanks for your consideration.


Followup   April 4, 2008 - 10am Central time zone:

no, plsql can write a file on the SERVER.

plsql runs in the server.

That said, you need "something" on the desktop to

a) connect to oracle
b) invoke the plsql


Now, once you tell me what that is, we can start talking about HOW to do this.


If you just need to extract a CSV file on a client, and you have sqlplus installed, you can just put into a file:


spool data.txt
select a || ',' || b || ',' || c ...
from t;
spool off
exit

and
sqlplus username/password@database @thatfile.sql


it'll create a file data.txt in your current directory

2 stars Tom replied cryptically with a question.   May 14, 2008 - 3pm Central time zone
Reviewer: Allen from Michigan, USA
Cryptic to me anyway. The question, PRS asked wasn't answered. We recently split servers and I had 
to stop using EXTERNAL defined tables.  No one here could figure out how to use that or UTL_FILE in 
that configuration. I and our DBA have been searching and searching (or googling and googling). 
I've seen others ask on various sites and NO ANSWER.  You had no answer either but rather, and 
excuse me for saying it, seemed to deflect the topic. It is a serious problem for us. 
PRS asked:
   Can I use directory object to create file using UTL_FILE on a different server(Application 
Server) than the database server?  
Tom replied:
only if the database server can SEE that directly itself
otherwise, it would be very virus like, wouldn't it.  How would you like it if files just started 
appearing on your machine magically??? 


Followup   May 16, 2008 - 10am Central time zone:

Allen - sorry, when I wrote

only if the database server can SEE that directly itself

I meant to say "directory", not "directly"

IF the database server cannot see the file system, then code running IN THAT DATABASE SERVER cannot see the file system. The file system does not exist.

pretend for a moment it could. So, my database - asktom on asktom.oracle.com should be able to see your file system?


If it could, think about it for a moment, would that not be very virus like.

I did not deflect anything, I said "of course NOT"


Think about this for a moment, how could it work - if the machine the database software is on has no access to this file system, how could a process that is part of the database software see this file system?


You have to make the file system available to the database server if you want the database server to have access to the file system.


No deflection
No obfuscation

The answer is: make the file system available to the database server.


If you say "we cannot, we split the machines"

Then all we can say is "sorry, we cannot do magic, you need to find a way to get the files available to the database server OR find another way entirely to do it"
4 stars Oracle unhandled userdefined exception while executing utl_files   June 30, 2008 - 7am Central time zone
Reviewer: sudhakar from India
Dear Tom,
      Iam facing a problem while executing the utl file
the snapshot of the error is given below

  ERROR at line 1:
ORA-06510: PL/SQL: unhandled user-defined exception
ORA-06512: at "SYS.UTL_FILE", line 120
ORA-06512: at "SYS.UTL_FILE", line 204
ORA-06512: at line 4

  The source code is
  create or replace procedure utl_file_test_write (
  path    in varchar2,
  filename  in varchar2,
  firstline in varchar2,
  secondline in varchar2)
is
  output_file utl_file.file_type;
begin
  output_file := utl_file.fopen (path,filename, 'W');
  utl_file.put_line (output_file, firstline);
  utl_file.put_line (output_file, secondline);
  utl_file.fclose(output_file);
  --exception
  -- when others then null;
end;




Followup   July 6, 2008 - 6pm Central time zone:

okley dokley, what is your line 4 then, that would be pretty "telling" no?



Oh, but wait, your line 4 in that example is part of the parameter list...

what is YOUR line 4.
5 stars Flat file reading   August 6, 2008 - 7am Central time zone
Reviewer: Sanjeev from Bangalore,India
Hi Tom,
I have a flat file which contains a signle record with demilited by ','.

Now I wanted to create a oracle table using this record:

e.g: Flat file record looks like:

Brand,Model,2006/01(Unit),2006/02(Unit),2006/03(Unit) and this goes till 36 Months i.e 
2008/12(Unit).

Now my Table needs to be like:

Table_test having columns as (Brand varchar2(25),Model  varchar2(50),2006/01(Unit) number and so on 
till 36 months).

Please remember that flat file columns are not fixed, which means now we have received a file 
containing 36 months as given above, but in future we may a get a file containing 24 months. So how 
to create a oracle table using such file values.

Thanks in advance.


Followup   August 6, 2008 - 9am Central time zone:

then you would have to

a) read flat file
b) parse flat file
c) determine how many columns you have
d) create a table
e) load that table using the data you just parsed.


no magic.

alternatively, you could set of a table

t( brand, model, c1, c2, c3, ..... c998 );

and use an external table with "trailing nullcols" - so that we'd fill in as many columns as we see in the file - leaving the rest NULL.
5 stars Reading flat file   August 7, 2008 - 3am Central time zone
Reviewer: Sanjeev from Bangalore,India
Hi Tom,
Thanks for your useful tips,
I have written the below procedure, which is getting failed while reading the file...

Procedure:

 CREATE OR REPLACE PROCEDURE PROC_LOAD IS
      v_file     UTL_FILE.file_type;
      err_file   UTL_FILE.file_type;                      
      l_file     VARCHAR2 (100)     := 'proc_test1.txt';
      e_file     VARCHAR2 (100)     := 'err_file.txt';
      v_how      VARCHAR2 (2)       := 'r';
      v_text     VARCHAR2 (32767);
      v_text_1     VARCHAR2 (32767);
      
      v_size     NUMBER             := 32767;
      V_COUNT NUMBER:=1;
      V_INCR NUMBER:=1;
      v_decr number;
      v_number    number;
      

   BEGIN

      
      v_file := UTL_FILE.fopen ('TEST', l_file, v_how,v_size);  
      err_file := UTL_FILE.fopen ('TEST', e_file, 'w',v_size);
      UTL_FILE.get_line (v_file, v_text);
      
      FOR I IN 1..100 LOOP
      
      if i=1 then
      
          V_INCR:=INSTR(V_TEXT,',',1,1);
          v_decr:=instr(v_text,',',1,I)-1;
          V_TEXT_1:=SUBSTR(V_TEXT,1,V_DECR);
          
      else
          V_INCR:=INSTR(V_TEXT,',',1,I-1);
          v_decr:=instr(v_text,',',1,I)-1;
                
          v_number:=v_decr-v_incr;
          
          
          V_TEXT_1:=SUBSTR(V_TEXT,V_INCR+1,v_number);
      END IF;
          
          UTL_FILE.put_line (err_file, v_text_1);
      

          DBMS_OUTPUT.PUT_LINE(V_TEXT_1);
      
      END LOOP;
    
      BEGIN
      
      LOOP
      
            BEGIN
               UTL_FILE.get_line (v_file, v_text);
            EXCEPTION
               WHEN NO_DATA_FOUND
               THEN
                  EXIT;
            END;
            
              
                 
      END LOOP;

         COMMIT;
      EXCEPTION
         WHEN OTHERS
         THEN
            UTL_FILE.put_line (err_file, 'Err : ' || SQLERRM);
      END;
    
      UTL_FILE.fclose (v_file);                                  -- Close File
      UTL_FILE.fclose (err_file);
     
   EXCEPTION
      WHEN UTL_FILE.invalid_path
      THEN
         DBMS_OUTPUT.put_line ('Invalid Path');
         UTL_FILE.fclose (v_file);                              -- Close File
         UTL_FILE.fclose (err_file);
      WHEN UTL_FILE.invalid_mode
      THEN
         DBMS_OUTPUT.put_line ('Invalid Mode');
         UTL_FILE.fclose (v_file);                              -- Close File
         UTL_FILE.fclose (err_file);
      WHEN UTL_FILE.write_error
      THEN
         DBMS_OUTPUT.put_line ('Write Error');
         UTL_FILE.fclose (v_file);                              -- Close File
         UTL_FILE.fclose (err_file);
      WHEN UTL_FILE.invalid_operation
      THEN
         DBMS_OUTPUT.put_line (SQLCODE || ': ' || SQLERRM);
         UTL_FILE.fclose (v_file);                              -- Close File
         UTL_FILE.fclose (err_file);
      WHEN OTHERS
      THEN
         DBMS_OUTPUT.put_line (SQLCODE || ': ' || SQLERRM);
         UTL_FILE.fclose (v_file);                              -- Close File
         UTL_FILE.fclose (err_file);
   END PROC_LOAD;

Input File:

Category,Brand,Model,Item (JAN),Launch Date,WIDTH,MONITOR SCREEN SIZE,MOVIE,ISO Sensitivity,Short 
Focal Distance,F Value,Long Focal Distance,OPTICAL ZOOM,GROUP OF OPTICAL ZOOM,DIGITAL ZOOM,MONITOR 
SCREEN,CCD TOTAL PIXELS,GROUP OF PIXEL TOTAL,WEIGHT,HEIGHT,DEPTH,WATER PROOF,STORAGE MEDIA,POWER 
SOURCE,MOVIE CODEC,CRADLE,FINDER,SENSOR TYPE,CCD MOVIE PIXELS,GROUP OF DVD EFFECTIVE 
PIXELS,EFFECTIVE PIXEL,GROUP OF EFFECTIVE PIXEL,MUSIC COMPRESSION FUNCTION TYPE,USB TYPE,CHANGEABLE 
LENS,WIRELESS COMMUNICATION,IMAGE STABILIZER,PICTBRIDGE,FRAME PER SECOND,MOVIE RECORDING 
PIXELS,CCD,LCD MONITOR PIXELS,Maximum Photographing Shots,INFRARED,PRINTER BUNDLE,LENS SET,SHUTTER 
SPEED MAX,SHUTTER SPEED MIN,NUMBER OF HDMI NEW,FACE DETECTION,SHOCKPROOF 
RECORDING,DUST-PROOF,Total(Unit),2006/01(Unit),2006/02(Unit),2006/03(Unit),2006/04(Unit),2006/05(Uni
t),2006/06(Unit),2006/07(Unit),2006/08(Unit),2006/09(Unit),2006/10(Unit),2006/11(Unit),2006/12(Unit)
,2007/01(Unit),2007/02(Unit),2007/03(Unit),2007/04(Unit),2007/05(Unit),2007/06(Unit),2007/07(Unit),2
007/08(Unit),2007/09(Unit),2007/10(Unit),2007/11(Unit),2007/12(Unit),Total(Value),2006/01(Value),200
6/02(Value),2006/03(Value),2006/04(Value),2006/05(Value),2006/06(Value),2006/07(Value),2006/08(Value
),2006/09(Value),2006/10(Value),2006/11(Value),2006/12(Value),2007/01(Value),2007/02(Value),2007/03(
Value),2007/04(Value),2007/05(Value),2007/06(Value),2007/07(Value),2007/08(Value),2007/09(Value),200
7/10(Value),2007/11(Value),2007/12(Value),Total(ASP),2006/01(ASP),2006/02(ASP),2006/03(ASP),2006/04(
ASP),2006/05(ASP),2006/06(ASP),2006/07(ASP),2006/08(ASP),2006/09(ASP),2006/10(ASP),2006/11(ASP),2006
/12(ASP),2007/01(ASP),2007/02(ASP),2007/03(ASP),2007/04(ASP),2007/05(ASP),2007/06(ASP),2007/07(ASP),
2007/08(ASP),2007/09(ASP),2007/10(ASP),2007/11(ASP),2007/12(ASP)

Output:

12:59:13 SQL> exec proc_load;
-29283: ORA-29283: invalid file operation
ORA-06512: at "SYS.UTL_FILE", line
475
ORA-29283: invalid file operation

PL/SQL procedure successfully completed.

Mainly in the above procedure I am trying to create a pivoted file using the above souce file like:
Brand
Level
2006/01(Unit)
2006/02(Unit)
.
.
.
2007/12(ASP)

As I mentioned in my earlier message that the date values are not fixed and I need to use these 
dates to created a pivoted table.

Thanks


3 stars UTL_FILE problem   August 8, 2008 - 2am Central time zone
Reviewer: Marcus from LA / Bavaria
Hello Sanjeev,
can you access the files at all? Is there a directory named 'Test'? The problem maybe is not the 
procedure logik but the access to the files.

Regards
Marcus


5 stars Reading Flat File   August 8, 2008 - 7am Central time zone
Reviewer: Sanjeev from Bangalore,India
Hi Marcus,

I am able to access the flat file provided If I delete some of the columns in the flat file as I 
believe the length of the line is too large to handle for UTL_FIle.fopen function.

'Test' is an directory which I have created for accessing the flat file.

So let me know is there any way by which I can access all the columns of that line from the flat 
file so that I can create an oracle table using those columns. This table I will be using to make 
pivoting of the measures based on the time dimension( Which is the part of the column mentioned in 
the flat file e.g 2006/01 (Unit) in which 2006/01 is the value belongs to time dimension).

Thanks.


Followup   August 8, 2008 - 1pm Central time zone:

utl_file is limited to 32k.

you should do it the way I told you to, external tables.
5 stars Unloading data to a flat file with PL/SQL   August 16, 2008 - 3am Central time zone
Reviewer: rc from The Netherlands
This article shows different ways of unloading data to a flat file with PL/SQL and it compares the 
performance of those different ways.

http://www.oracle-developer.net/display.php?id=425  


3 stars Oracle 10g Flat File   August 19, 2008 - 6pm Central time zone
Reviewer: Mike from Monterrey, MX
Hi! i've got oracle 10g and i need to create a flat file to feed sap... anyway this is what i been 
trying... pls help me

create or replace procedure Cursos_Loading(p_nothing   IN integer)
  is

v_filehandle  utl_file.file_type;
 
begin
  v_filehandle := utl_file.fopen('DIRECTORY', 'FILENAME', 'w', 4000);
    for r in 1..10 loop
      utl_file.put_line(v_filehandle, 'LINE # : '||r);
    end loop;
  utl_file.fclose(v_filehandle);

 
end Cursos_Loading;

what i'm doing wrong 'cause when i execute the sproc appears this error...

/* Source of PACKAGE BODY SYS.UTL_FILE is not available */
ORA-29280 Invalid directory path


Followup   August 20, 2008 - 10am Central time zone:

well, you obviously have an invalid directory path??

did you

create or replace directory 'DIRECTORY' as '/x/y/z';

please don't use /x/y/z of course....
4 stars   September 29, 2008 - 8am Central time zone
Reviewer: Rajeev from India
Hi
I have created a procedure to extract data from Oracle to XML file. It is working with a problem 
that, once the XML file reached the size of 34KB it is not writing any more data. Your suggetions 
will be helpful. 
The procedure is follows

CREATE OR REPLACE PROCEDURE extractToXML(pTname varchar2,rowSettag varchar2,rowTag 
varchar2,filename varchar2)
IS
BEGIN
declare
   v_ctx   dbms_xmlquery.ctxtype;
   v_file   utl_file.file_type;
   v_xml   clob;
   v_more   boolean := true;
   v_date   varchar2(30);
BEGIN
   v_date := to_char(systimestamp - 29, 'yyyy-mm-dd')||' 00:00:00';
   -- create xml context.
   v_ctx := dbms_xmlquery.newcontext('select * from '||pTname);
  -- dbms_xmlquery.setxslt(v_ctx, '/home/dwintuser/Development/XML/dvBatchFile.xsd') ;
   -- create the transformed html document.
   dbms_xmlquery.setRowSetTag(v_ctx,rowSettag);
   dbms_xmlquery.setRowTag(v_ctx,rowTag);
   v_xml := dbms_xmlquery.getxml(v_ctx);
   dbms_xmlquery.closecontext(v_ctx);
   -- output html document to file
   v_file := utl_file.fopen('C:\oraload', filename, 'W',32767);
   while v_more loop
      utl_file.put(v_file, substr(v_xml, 1, 32767));
    dbms_output.put_line('Length of XML '||length(v_xml));
      if length(v_xml) > 32767 
      then
         v_xml :=  substr(v_xml, 32768);
    dbms_output.put_line('Inside IF Length of XML '||length(v_xml));
      else
         v_more := false;
      end if;
   end loop;
   utl_file.fclose(v_file);
   dbms_output.put_line('Coke Completed Successfully');
exception
   when others then
      dbms_output.put_line(substr(sqlerrm,1,1000));
      utl_file.fclose(v_file);
end;
END;


Followup   September 29, 2008 - 9am Central time zone:

utl_file demands a newline at least every 32k.

do you have any?
4 stars Writing Big files - Suggest a better way   January 27, 2009 - 3am Central time zone
Reviewer: Nishant from India
Hi Tom,
  We have been using UTL_FILE to write to text files from our oracle tables.A shell script 
internally transfers the file to Mainframe based daa mart system.We do this on a daily basis.

Our files are too large and on an average their size remains about 40 GB.Sometimes it goes upto 90 
GB when data is accumulated for multiple days.

My table from where I dump the data is Parallel enable with degree 8 and we are using ASSM.

This consumes lot of time , 4-5 hrs(sometimes even more) in file writing.Can you please suggest us 
a better way of doing this ?



Followup   January 28, 2009 - 8am Central time zone:

plsql would probably be my last choice for this task. Actually, I cannot imagine dumping 40-90gb to a flat file - that seems like a "bad idea". But whatever.

http://asktom.oracle.com/tkyte/flat/index.html

5 stars Big file dumping   January 29, 2009 - 5am Central time zone
Reviewer: Nishant from India
Thank you very much Tom.However,It is like sending each commissionable transaction from Oracle 
incentive compensation tables (approx 1 million trxs daily are commissionable out of total approax 
8 million transactions in our POS system) to reporting system on mainframe.
If we send the data for more than 4-5 weeks, then this problem arises.
If you find some better way to do this,please share with me.

Thanks again for your time and effort.


Followup   January 30, 2009 - 2pm Central time zone:

well, I laid out options in that link for dumping to a file.

My point was "probably do not need mainframe reporting system", you already have the data in a pretty competent reporting system.
5 stars parallel dumping with a ref_cursor   February 21, 2009 - 12pm Central time zone
Reviewer: Neil from London
Hi tom -
I wanted to dump a file onto my server in double-quick time, after reading some good stuff on 
www.oracle-developer.net which I have attempted to implement.
Why can I parallelize a result set in the package below
using cursor(select...), but not if I pass in a ref_cursor. 
Is this a limitation, or am I doing something stupid? 
Here is a reproducuble test: (sorry it's more than 1000 chars)

begin
  execute immediate 'alter session enable parallel query';
  execute immediate 'alter session enable parallel dml';
end;
/
create table source_table
parallel
nologging
as
   select a.text
     from all_source a
    where rownum < 1001
/
create 
  type dump_results_typ 
    as object(file_name  varchar2(255)
             ,no_records number
             ,session_id number)
/
create 
  type dump_results_tab_typ 
    as table of dump_results_typ
/
create or replace package parallel_dump is
  function fn_dump (i_directory in varchar2
                   ,i_file      in varchar2)
    return integer; 
  function fn_cur
    return sys_refcursor;
  function fn_read_and_dump (i_directory in varchar2
                            ,i_file      in varchar2
                            ,i_cursor    in sys_refcursor)
    return dump_results_tab_typ pipelined 
                                parallel_enable 
                               (partition i_cursor by any);
end parallel_dump;
/

create or replace package body parallel_dump is
  function fn_dump (i_directory in varchar2
                   ,i_file      in varchar2)
    return integer
  is
    l_cursor  sys_refcursor;
    l_results dump_results_tab_typ := dump_results_tab_typ();
  begin
    l_cursor  := fn_cur;
    -- this doesn't seem to work...
    select /*+ parallel(t,4) */
           dump_results_typ(file_name, no_records, session_id)
      bulk collect 
      into l_results
      from table(parallel_dump.fn_read_and_dump(i_directory
                                               ,i_file
                                               ,l_cursor)) t; -- <== Variable
    if l_results.count > 0
    then
      for i in l_results.first..l_results.last
      loop
         dbms_output.put_line(to_char(l_results(i).no_records)
                              ||' records written to '||l_results(i).file_name);
      end loop;
    end if;
    -- but this does!!!
    select /*+ parallel(t,4) */
           dump_results_typ(file_name, no_records, session_id)
      bulk collect 
      into l_results
      from table(parallel_dump.fn_read_and_dump(i_directory
                                               ,i_file
                                               ,cursor(select * from source_table))) t;
    if l_results.count > 0
    then
      for i in l_results.first..l_results.last
      loop
         dbms_output.put_line(to_char(l_results(i).no_records)
                              ||' records written to '||l_results(i).file_name);
      end loop;
    end if;
    return 0;
  exception
    when others then
      return 1;
  end fn_dump;

  function fn_cur
    return sys_refcursor
  is 
    l_cursor sys_refcursor;
  begin
    open l_cursor for
    select * from source_table;
    return l_cursor;  
  end fn_cur;
  function fn_read_and_dump (i_directory in varchar2
                            ,i_file      in varchar2
                            ,i_cursor    in sys_refcursor)
    return dump_results_tab_typ 
    pipelined 
    parallel_enable (partition i_cursor by any) 
  as
     type      output_tab_typ is table of varchar2(32767);
     l_rows    output_tab_typ;
     l_file    utl_file.file_type;
     l_name    varchar2(255);
     l_buffer  varchar2(32767);
     l_sid     number;
     l_lines   pls_integer          := 0;
     c_eol     constant varchar2(1) := chr(10);
     c_eollen  constant pls_integer := length(c_eol);
     c_maxline constant pls_integer := 32767;
  begin
     select sid 
       into l_sid 
       from v$mystat 
      where rownum = 1;
     l_name := i_file || '_' || to_char(l_sid) || '.csv';
     l_file := utl_file.fopen(i_directory, l_name, 'w', 32767);
     loop
       fetch i_cursor 
        bulk collect 
        into l_rows 
       limit 250;
       for i in 1 .. l_rows.count 
       loop
         if length(l_buffer) + c_eollen + length(l_rows(i)) <= c_maxline 
         then
           l_buffer := l_buffer || c_eol || l_rows(i);
         else
           if l_buffer is not null 
           then
             utl_file.put_line(l_file, l_buffer);
           end if;
           l_buffer := l_rows(i);
         end if;
       end loop;
       l_lines := l_lines + l_rows.count;
       exit when i_cursor%notfound;
     end loop;
     close i_cursor;
     utl_file.put_line(l_file, l_buffer);
     utl_file.fclose(l_file);
     pipe row (dump_results_typ(l_name, l_lines, l_sid));
     return;
  exception 
    when others then 
      if i_cursor%isopen then close i_cursor; end if;
      raise_application_error(-20500, 'Unknown error in parallel_dump.fn_read_and_dump: '
                                      ||sqlerrm(sqlcode));
  end fn_read_and_dump;
begin
  null;
end parallel_dump;
/
/*=============================================================================================*/
SQL*Plus: Release 10.2.0.1.0 - Production on Fri Feb 20 16:41:01 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - 64bit Production
With the Partitioning, OLAP and Data Mining options

SQL*Plus> declare
  2    res number;
  3  begin
  4
  5    res := parallel_dump.fn_dump(i_directory => '/your/directory/here'
  6                                ,i_file      => 'pdtest1');
  7
  8* end;
  9 /

1000 records written to pdtest1_521.csv --<== No parallism with ref_cursor variable
560 records written to pdtest1_526.csv
102 records written to pdtest1_495.csv
236 records written to pdtest1_513.csv
102 records written to pdtest1_525.csv

PL/SQL procedure successfully completed.



Followup   February 21, 2009 - 9pm Central time zone:

I hate your code
http://asktom.oracle.com/pls/ask/search?p_string=%22i+hate+your+code%22


  exception
    when others then
      return 1;
  end fn_dump;





do you have access to "Expert Oracle Database Architecture", I have a full example in there.


and sort of try to explain what is going on. We cannot magically execute your PLSQL in parallel, think about it - look at this query:

    select /*+ parallel(t,4) */
           dump_results_typ(file_name, no_records, session_id)
      bulk collect 
      into l_results
      from table(parallel_dump.fn_read_and_dump(i_directory
                                               ,i_file
                                               ,l_cursor)) t; -- <== Variable


How could we call your code in N sessions - and each of it executes a slice of the data? What is being 'parallelized' here? You are asking us to execute a slice of your function fn_read_and_dump in 4 different sessions - how could that be possible? There is no way we have to tell your code "hey, you are to return the 3rd quarter of the data - now do your stuff. Even if we could - how would YOU know how to get the 3rd quarter?



We can execute queries in parallel and send the slices of data to your routines (execute your routine against each bit of the parallel result set.

We cannot execute your code in parallel.


we only know what "parallel query" is.



1 stars Still don't get it   February 22, 2009 - 5pm Central time zone
Reviewer: Neil from London
The examples need to be succinct, so the exception handling got chucked.
I still don't understand why a cursor variable passed into an exposed packaged function doesn't work and passing in "cursor(select...)" does.

  open l_cursor for select * from source_table;
  :
  select /*+ parallel(t,4) */
      dump_results_typ(file_name, no_records, session_id)
    bulk collect
    into l_results
    from table(parallel_dump.fn_read_and_dump(i_directory
                              ,i_file
                              ,l_cursor)) t; -- <== Variable - no good!

  -- But this is ok! Parallelises nicely...
  select /*+ parallel(t,4) */
      dump_results_typ(file_name, no_records, session_id)
    bulk collect
    into l_results
    from table(parallel_dump.fn_read_and_dump(i_directory
                              ,i_file
                              ,cursor(select * from source_table))) t;

After all, called from the same block, the pl/sql function is receiving the SAME information in the last argument, just in a different syntax.
Maybe the question should be "what's the difference between a cursor variable and a select statement cast as a cursor, to a parallel pipelined function?"


Followup   February 22, 2009 - 6pm Central time zone:

the exception handling was not chucked, it was turned into a horrible worst practice. to chuck it would be to remove it, I would have loved to see it removed.

I have simply vowed to myself that anytime I see when others not followed by raise/raise application error used improperly to write "i hate your code" and link to the growing library of examples.


Anyway


select /*+ parallel(t,4) */
      dump_results_typ(file_name, no_records, session_id)
    bulk collect
    into l_results
    from table(parallel_dump.fn_read_and_dump(i_directory
                              ,i_file
                              ,l_cursor)) t; -- <== Variable - no good! 



you are trying to ask us to run your function in parallel. The BEST we could do is call it N times, but it would run from START TO FINISH N times (there is nothing for us to divvy up and send to you, we cannot send your routine a SLICE OF DATA, the only thing we know how to slice and dice is SQL and you don't have any SQL for us to SLICE UP). The sql is already opened, already run, already executed, it is out of our control - we are NOT opening it there, someone else did and frankly - we cannot tell if that SQL is parallel or not - it doesn't matter if it is - we already started executing it, it is going - it is too late to play games with it.


we have NO way to slice up the data to be processed by each copy, if we did parallel 4 - you would get 4 complete copies of the output (that would be *wrong*). There is NOTHING we could 'send' to your routine to tell the 3rd copy to just get the 3rd quarter of the data to process.

versus:


  -- But this is ok! Parallelises nicely...
  select /*+ parallel(t,4) */
      dump_results_typ(file_name, no_records, session_id)
    bulk collect
    into l_results
    from table(parallel_dump.fn_read_and_dump(i_directory
                              ,i_file
                              ,cursor(select * from source_table))) t;



We can, because you stated so in your declaration, slice up "select * from source_table" in any way we see fit and run N copies of your routine against each slice. Very very very different. We have some SQL to slice up, we do so and we invoke your routine N times - each with a different slice of data to process.





The only way to run your code in parallel is to take a query that is an INPUT to the code and parallelize the query and send a slice of the output to each copy of your routine. In order to do that, we need to control very finely the opening of that cursor - to parallelize it with the code.


You cannot open and the change the way it should be opened after the fact. You have to combine the opening of the query with the invocation of the routine itself.




3 stars ref_cursor and cursor(select...)   February 23, 2009 - 4am Central time zone
Reviewer: Neil from London
To sum up then, a pipelined parallel function declaration MUST have a ref_cursor as a parameter, 
but that parameter CANNOT be opened before it is passed to the function; and the only way to pass a 
ref_cursor without opening it is to employ a CURSOR(select...) expression.




Followup   February 23, 2009 - 4am Central time zone:

I agree that the documentation:

http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14289/dcitblfns.htm#i1005017


is lacking/not clear in that fact, but yes, the cursor() construct is necessary - so that the entire outer query can be parallelized with it. In fact, I wish they would not have used 'ref cursor' at all in the parallel discussion but rather just "cursor variable"

You have to sort of think of your query:


select * from table( cursor( select * from t ) );

and being broken up into:


select * from table( cursor( select * from t where rowid between :a and :b ) );
select * from table( cursor( select * from t where rowid between :a and :b ) );
select * from table( cursor( select * from t where rowid between :a and :b ) );
select * from table( cursor( select * from t where rowid between :a and :b ) );

and that is what is actually run in parallel (conceptually). If you send it an already opened result set, it is too late to do that.
5 stars Use .tsv file as input to sql query and append the output to the .tsv file   April 24, 2009 - 2am Central time zone
Reviewer: John from Boston
Hi tom, Here is my scenario.. 

1) I have a sample.tsv file with some data as below,

dat1    dat2    dat3
-----   ----    -----
xyz1    xyz2    xyz3
xyz4    xyz5    xyz6

2) I need to have a script which takes in the above sample.tsv as input to sql query and the 
resulted output should be appended to the same sample.tsv file on the clinet side, I need not have 
this on server. The output will be just adding ID column to the sample.tsv file. 

Ex: 

select id from tab1 where col1 = xyz1 and col2 = xyz2; 
{xyz1 and xyz2 are the values from above sample.tsv file}

Required resulting sample.tsv file.

id    dat1    dat2    dat3 
---     ----    ----    ----
1    xyz1    xyz2    xyz3
2    xyz4    xyz5    xyz6

3) Now I pick the above sample.tsv file and use it as a input to some command to load the data of 
sample.tsv file to the  database. Hope I am clear. Please let me know if I am not. 

I am trying to have the above process automated. I am very new to Oracle and I want to know if your 
dump_csv script can handle this with modifications or if you have some other script which does 
something similar to this or if it is possible at all to achieve this in pl/sql, if how should be 
the approach. Please help.

Thanks,
John


Followup   April 27, 2009 - 11am Central time zone:

2) I need to have a script which takes in the above sample.tsv as input to sql
query and the resulted output should be appended to the same sample.tsv file on
the clinet side, I need not have this on server. The output will be just adding
ID column to the sample.tsv file.


you will be writing a client program then, frankly the database isn't even involved here - you'd have to get the file to the server and the server can only create files on the server.


You do not tell us what this client is
You do not tell us what software the client has


You cannot do this in plsql, since plsql runs on the server and the server cannot write to your filesystem.

Well, you could do this in plsql, if you are using something like mod_plsql - for example, I could write a stored procedure and put it on asktom to return tab delimited data. In fact, I could create a form that would let the client upload their file to the server and then the server returns to them a brand new file (no appending, we cannot write to YOUR filesystem - a program that runs on your client could, but we don't have anything there)
5 stars PL/SQL Performance (Bulk Collect)   July 29, 2009 - 10am Central time zone
Reviewer: Manuel Vidigal from Lisboa, Portugal
Hi Tom,

I changed you code a bit so that it could bulk collect any given query (it concatenates every column with the separator and replaces the columns with this concatenation). The only limitation is the maximum 4000 chars per line.

Here is the code:

CREATE OR REPLACE FUNCTION dump_csv_to_file_new(p_query     IN VARCHAR2,
                                                p_separator IN VARCHAR2 DEFAULT ';',
                                                p_dir       IN VARCHAR2,
                                                p_filename  IN VARCHAR2,
                                                p_header    IN VARCHAR2 DEFAULT 'Y',
                                                p_limit     IN INTEGER DEFAULT 100)
  RETURN NUMBER AUTHID CURRENT_USER IS
  TYPE csv_cur_type IS REF CURSOR;
  csv_cur csv_cur_type;
  TYPE myarray IS TABLE OF VARCHAR2(4000);
  l_data          myarray;
  l_query         VARCHAR2(4000);
  l_column_string VARCHAR2(2000);
  l_thecursor     INTEGER DEFAULT dbms_sql.open_cursor;
  l_columnvalue   VARCHAR2(2000);
  l_status        INTEGER;
  l_colcnt        NUMBER DEFAULT 0;
  l_separator_csv VARCHAR2(10);
  l_cnt           NUMBER DEFAULT 0;
  rec_tab         dbms_sql.desc_tab;
  l_output        utl_file.file_type;
  l_buffer        VARCHAR2(32767) := NULL;
  l_new_line      VARCHAR2(10) := chr(10);
BEGIN
  l_output := utl_file.fopen(p_dir, p_filename, 'w');
  dbms_sql.parse(l_thecursor, p_query, dbms_sql.native);
  FOR i IN 1 .. 255 LOOP
    BEGIN
      dbms_sql.define_column(l_thecursor, i, l_columnvalue, 2000);
      l_colcnt := i;
    EXCEPTION
      WHEN OTHERS THEN
        IF (SQLCODE = -1007) THEN
          EXIT;
        ELSE
          RAISE;
        END IF;
    END;
  END LOOP;
  dbms_sql.define_column(l_thecursor, 1, l_columnvalue, 2000);
  l_status := dbms_sql.EXECUTE(l_thecursor);
  dbms_sql.describe_columns(l_thecursor, l_colcnt, rec_tab);
  l_separator_csv := '';
  FOR x IN 1 .. l_colcnt LOOP
    l_column_string := l_column_string || l_separator_csv || rec_tab(x)
                      .col_name;
    l_separator_csv := '||''' || p_separator || '''||';
  END LOOP;
  IF nvl(p_header, 'Y') != 'N' THEN
    utl_file.put_line(l_output, l_column_string);
    l_cnt := l_cnt + 1;
  END IF;
  l_query := regexp_replace(upper(p_query),
                            '^SELECT (.*?) FROM',
                            'SELECT ' || l_column_string || ' FROM');
  dbms_output.put_line(l_query);
  OPEN csv_cur FOR l_query;
  LOOP
    FETCH csv_cur BULK COLLECT
      INTO l_data LIMIT p_limit;
    FOR j IN 1 .. l_data.COUNT LOOP
      IF length(l_buffer || l_data(j)) >= 30000 THEN
        utl_file.put_line(l_output, l_buffer);
        l_buffer := l_data(j);
      ELSE
        IF l_buffer IS NULL THEN
          l_buffer := l_data(j);
        ELSE
          l_buffer := l_buffer || l_new_line || l_data(j);
        END IF;
      END IF;
      l_cnt := l_cnt + 1;
    END LOOP;
    EXIT WHEN csv_cur%NOTFOUND;
  END LOOP;
  CLOSE csv_cur;
  utl_file.put_line(l_output, l_buffer);
  utl_file.fclose(l_output);
  RETURN l_cnt;
END dump_csv_to_file_new;

I performed some tests and it look pretty fast, I would like to compare it with the pro*C way, but I don't understand how to compile and use the pro*C.
Here is the performance test:
SQL> CREATE TABLE csv_test AS
  2  SELECT o.*
  3    FROM all_objects o,
  4         (SELECT LEVEL a
  5            FROM dual
  6          CONNECT BY LEVEL <= 100);

Tabela criada.

Decorreram: 00:00:42.60

SQL>  select count(1) from csv_test;

  COUNT(1)
----------
   6837100

Decorreram: 00:00:14.60
SQL> set autotrace traceonly statistics
SQL> select * from csv_test;

6837100 linhas seleccionadas.

Decorreram: 00:01:05.79

Statistics
----------------------------------------------------------
          4  recursive calls
          0  db block gets
     549970  consistent gets
     100707  physical reads
          0  redo size
  807972849  bytes sent via SQL*Net to client
    5014386  bytes received via SQL*Net from client
     455808  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
    6837100  rows processed

SQL> DECLARE
  2    l_result NUMBER;
  3  BEGIN
  4    l_result := dump_csv_to_file_new
  5                (p_query     => 'select * from csv_test',
  6                 p_separator => ';',
  7                 p_dir       => 'TEMP',
  8                 p_filename  => 'dump_csv_to_file_new.csv',
  9                 p_header    => 'N',
 10                 p_limit     => 100);
 11  END;
 12  /

Procedimento de PL/SQL concluÝdo com Ûxito.

Decorreram: 00:01:15.03
SQL> DECLARE
  2    l_result NUMBER;
  3  BEGIN
  4    l_result := dump_csv
  5                (p_query => 'select * from csv_test',
  6                 p_separator => ';',
  7                 p_dir => 'TEMP',
  8                 p_filename => 'dump_csv.csv');
  9  END;
 10  /

Procedimento de PL/SQL concluÝdo com Ûxito.

Decorreram: 00:08:32.13


The modified code only takes more 10 seconds than the query itself. The output file has 697MB. So for cases where the limit per line is not bigger than 4000, I think one can use this new procedure instead of the pro*C program. Although I would like to see some one compare the two.

Cheers,
Manel

5 stars   August 4, 2009 - 1am Central time zone
Reviewer: A reader 
Hi Tom, for writing csv file iam using dump_csv function. I had a problem when my address is in the 
column like VALLEY SPRINGS, CA. while opening the csv file address is showing in two different 
columns. How to make this in a single column. Thanks in advance.



Followup   August 4, 2009 - 2pm Central time zone:

you will need to probably put double quotes around it.

Read up on what YOUR thing that opens your csv file is expecting as a data format - here is a good one if your thing is excel

http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm



4 stars Unload data on client side   August 6, 2009 - 3am Central time zone
Reviewer: Kapil Sharma from India
Hi Tom

I really liked this post and it does clear all the doubts and issues but 1 thing that I would like 
to ask is if there is any way to create flat files on the client server (and not on the DB server) 
by calling a pl/sql procedure?


Followup   August 6, 2009 - 8am Central time zone:

ctl-f on this page for the word

virus


and read the multitude of discussions on this topic we've already had.

Write a Review
 


All information and materials provided here are provided "as-is"; Oracle disclaims all express and implied warranties, including, the implied warranties of merchantability or fitness for a particular use. Oracle shall not be liable for any damages, including, direct, indirect, incidental, special or consequential damages for loss of profits, revenue, data or data use, incurred by you or any third party in connection with the use of this information or these materials.

About Oracle | Legal Notices and Terms of Use | Privacy Statement