This is how I dump data from an Oracle Database (tested on 8i,9i,10g,11g,12c) to a delimited ascii file:
SQL*Plus: Release 12.1.0.2.0 Production on Fri Feb 24 13:55:47 2017 Copyright (c) 1982, 2014, Oracle. All rights reserved. Connected to: Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production SQL> set timing on SQL> select Dump_Delimited('select * from all_objects', 'all_objects.csv') nr_rows from dual; NR_ROWS ---------- 97116 Elapsed: 00:00:11.87 SQL> ! cat /u01/etl/report/all_objects_readme.txt ********************************************************************* Record Layout of file /u01/etl/report/all_objects.csv ********************************************************************* Column Sequence MaxLength Datatype ------------------------------ -------- --------- ---------- OWNER 1 128 VARCHAR2 OBJECT_NAME 2 128 VARCHAR2 SUBOBJECT_NAME 3 128 VARCHAR2 OBJECT_ID 4 24 NUMBER DATA_OBJECT_ID 5 24 NUMBER OBJECT_TYPE 6 23 VARCHAR2 CREATED 7 20 DATE LAST_DDL_TIME 8 20 DATE TIMESTAMP 9 19 VARCHAR2 STATUS 10 7 VARCHAR2 TEMPORARY 11 1 VARCHAR2 GENERATED 12 1 VARCHAR2 SECONDARY 13 1 VARCHAR2 NAMESPACE 14 24 NUMBER EDITION_NAME 15 128 VARCHAR2 SHARING 16 13 VARCHAR2 EDITIONABLE 17 1 VARCHAR2 ORACLE_MAINTAINED 18 1 VARCHAR2 ---------------------------------- Generated: 24-02-2017 13:56:50 Generated by: ETL Columns Count: 18 Records Count: 97116 Delimiter: ][ Row Delimiter: ] ---------------------------------- SQL>
Next to the query and the generated filename the Dump_Delimited function takes another 6 parameters, each one with a default value. Check out the PL/SQL, and BTW… the basics for this code comes from Tom Kyte.
SET DEFINE OFF; CREATE OR REPLACE DIRECTORY ETL_UNLOAD_DIR AS '/u01/etl/report'; GRANT READ, WRITE ON DIRECTORY ETL_UNLOAD_DIR TO ETL; CREATE OR REPLACE FUNCTION Dump_Delimited ( P_query IN VARCHAR2 , P_filename IN VARCHAR2 , P_column_delimiter IN VARCHAR2 := '][' , P_row_delimiter IN VARCHAR2 := ']' , P_comment IN VARCHAR2 := NULL , P_write_rec_layout IN PLS_INTEGER := 1 , P_dir IN VARCHAR2 := 'ETL_UNLOAD_DIR' , P_nr_is_pos_integer IN PLS_INTEGER := 0 ) RETURN PLS_INTEGER IS filehandle UTL_FILE.FILE_TYPE; filehandle_rc UTL_FILE.FILE_TYPE; v_user_name VARCHAR2(100); v_file_name_full VARCHAR2(200); v_dir VARCHAR2(200); v_total_length PLS_INTEGER := 0; v_startpos PLS_INTEGER := 0; v_datatype VARCHAR2(30); v_delimiter VARCHAR2(10):= P_column_delimiter; v_rowdelimiter VARCHAR2(10):= P_row_delimiter; v_cursorid PLS_INTEGER := DBMS_SQL.OPEN_CURSOR; v_columnvalue VARCHAR2(4000); v_ignore PLS_INTEGER; v_colcount PLS_INTEGER := 0; v_newline VARCHAR2(32676); v_desc_cols_table DBMS_SQL.DESC_TAB; v_dateformat NLS_SESSION_PARAMETERS.VALUE%TYPE; v_stat VARCHAR2(1000); counter PLS_INTEGER := 0; BEGIN SELECT directory_path INTO v_dir FROM DBA_DIRECTORIES WHERE directory_name = P_dir; v_file_name_full := v_dir||'/'||P_filename; SELECT VALUE INTO v_dateformat FROM NLS_SESSION_PARAMETERS WHERE parameter = 'NLS_DATE_FORMAT'; /* Use a date format that includes the time. */ v_stat := 'alter session set nls_date_format=''dd-mm-yyyy hh24:mi:ss'' '; EXECUTE IMMEDIATE v_stat; filehandle := UTL_FILE.FOPEN( P_dir, P_filename, 'w', 32000 ); /* Parse the input query so we can describe it. */ DBMS_SQL.PARSE( v_cursorid, P_query, dbms_sql.native ); /* Now, describe the outputs of the query. */ DBMS_SQL.DESCRIBE_COLUMNS( v_cursorid, v_colcount, v_desc_cols_table ); /* For each column, we need to define it, to tell the database * what we will fetch into. In this case, all data is going * to be fetched into a single varchar2(4000) variable. * * We will also adjust the max width of each column. */ IF P_write_rec_layout = 1 THEN filehandle_rc := UTL_FILE.FOPEN(P_dir, SUBSTR(P_filename,1, INSTR(P_filename,'.',-1)-1)||'_readme.txt', 'w'); --Start Header v_newline := CHR(10)||CHR(10)||' ********************************************************************* '; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Record Layout of file '||v_file_name_full; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' ********************************************************************* '||CHR(10)||CHR(10); UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Column Sequence MaxLength Datatype '; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' ------------------------------ -------- --------- ---------- '||CHR(10); UTL_FILE.PUT_LINE(filehandle_rc, v_newline); --End Header --Start Body FOR i IN 1 .. v_colcount LOOP DBMS_SQL.DEFINE_COLUMN( v_cursorid, i, v_columnvalue, 4000 ); SELECT DECODE( v_desc_cols_table(i).col_type, 2, DECODE(v_desc_cols_table(i).col_precision,0,v_desc_cols_table(i).col_max_len,v_desc_cols_table(i).col_precision)+DECODE(P_nr_is_pos_integer,1,0,2) , 12, 20, v_desc_cols_table(i).col_max_len ) INTO v_desc_cols_table(i).col_max_len FROM dual; SELECT DECODE( TO_CHAR(v_desc_cols_table(i).col_type), '1' , 'VARCHAR2' , '2' , 'NUMBER' , '8' , 'LONG' , '11' , 'ROWID' , '12' , 'DATE' , '96' , 'CHAR' , '108', 'USER_DEFINED_TYPE', TO_CHAR(v_desc_cols_table(i).col_type) ) INTO v_datatype FROM DUAL; v_newline := RPAD(' '||v_desc_cols_table(i).col_name,34)||RPAD(i,10)||RPAD(v_desc_cols_table(i).col_max_len,11)||RPAD(v_datatype,25); UTL_FILE.PUT_LINE(filehandle_rc, v_newline); END LOOP; --End Body ELSE FOR i IN 1 .. v_colcount LOOP DBMS_SQL.DEFINE_COLUMN( v_cursorid, i, v_columnvalue, 4000 ); SELECT DECODE( v_desc_cols_table(i).col_type, 2, DECODE(v_desc_cols_table(i).col_precision,0,v_desc_cols_table(i).col_max_len,v_desc_cols_table(i).col_precision)+DECODE(P_nr_is_pos_integer,1,0,2) , 12, 20, v_desc_cols_table(i).col_max_len ) INTO v_desc_cols_table(i).col_max_len FROM dual; END LOOP; END IF; v_ignore := DBMS_SQL.EXECUTE(v_cursorid); WHILE ( DBMS_SQL.FETCH_ROWS(v_cursorid) > 0 ) LOOP /* Build up a big output line. This is more efficient than * calling UTL_FILE.PUT inside the loop. */ v_newline := NULL; FOR i IN 1 .. v_colcount LOOP DBMS_SQL.COLUMN_VALUE( v_cursorid, i, v_columnvalue ); if i = 1 then v_newline := v_newline||v_columnvalue; else v_newline := v_newline||v_delimiter||v_columnvalue; end if; END LOOP; /* Now print out that line and increment a counter. */ UTL_FILE.PUT_LINE( filehandle, v_newline||v_rowdelimiter ); counter := counter+1; END LOOP; IF P_write_rec_layout = 1 THEN --Start Footer v_newline := CHR(10)||CHR(10)||' ---------------------------------- '; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Generated: '||SYSDATE; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Generated by: '||USER; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Columns Count: '||v_colcount; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Records Count: '||counter; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Delimiter: '||v_delimiter; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' Row Delimiter: '||v_rowdelimiter; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); v_newline := ' ---------------------------------- '||CHR(10)||CHR(10); UTL_FILE.PUT_LINE(filehandle_rc, v_newline); --End Footer --Start Commment v_newline := ' '||P_comment; UTL_FILE.PUT_LINE(filehandle_rc, v_newline); --End Commment UTL_FILE.FCLOSE(filehandle_rc); END IF; /* Free up resources. */ DBMS_SQL.CLOSE_CURSOR(v_cursorid); UTL_FILE.FCLOSE( filehandle ); /* Reset the date format ... and return. */ v_stat := 'alter session set nls_date_format=''' || v_dateformat || ''' '; EXECUTE IMMEDIATE v_stat; RETURN counter; EXCEPTION WHEN OTHERS THEN DBMS_SQL.CLOSE_CURSOR( v_cursorid ); EXECUTE IMMEDIATE 'alter session set nls_date_format=''' || v_dateformat || ''' '; RETURN counter; END Dump_Delimited; / SHOW ERRORS;
This is very helpful that how we dump data from an Oracle Database tested on all version of oracle to a delimited ascii file.
Thank you for this blog and sharing this important codes.
Keep posting.