You might find it quicker to use UTL_FILE, but probably not that much faster.
in my test it was slightly faster over about 20k of rows, blow that out over 14 million though and it might be worth it.
I believe if you want to get any quicker than this, the way to go would be pro*c.. but I haven't got into that, so can't really advise.
set pagesize 1000
set FLUSH OFF
drop user usera cascade;
create user usera default tablespace users identified by abc123;
grant create session to usera;
grant resource to usera;
create or replace directory testdir as '/tmp';
grant read,write on directory testdir to usera;
grant execute on UTL_FILE to usera;
connect usera/abc123;
set timing on
spool /tmp/spooltest.txt
select object_name from all_objects;
spool off
DECLARE
v_file UTL_FILE.FILE_TYPE;
TYPE t_col is table of all_objects.object_name%type index by PLS_INTEGER;
v_object_names t_col;
BEGIN
v_file := UTL_FILE.FOPEN('TESTDIR','utlfiletext.txt','w');
select object_name BULK COLLECT INTO v_object_names
from all_objects;
for idx IN 1 .. v_object_names.COUNT LOOP
UTL_FILE.PUT_LINE(v_file, v_object_names(idx), FALSE);
END LOOP;
UTL_FILE.FCLOSE(v_file);
END;
/
The results. The top result being from sqlplus only, the bottom using UTL_FILE
23931 rows selected.
Elapsed: 00:00:06.60
PL/SQL procedure successfully completed.
Elapsed: 00:00:05.45