read delimited file from pl sql procedure - oracle

I'm new to pl sql and trying to read a file(column wise),where file(abc.txt) has data delimited by pipe "|" like as below:
first test|add|123
second test|mod|654
So my requirement is like
How can we read above file in a pl sql procedure.
working sample code will be very helpful.
and below code i used for reading a file:
set serveroutput on;
DECLARE
V1 VARCHAR2(200);
F1 UTL_FILE.FILE_TYPE;
V1 VARCHAR2(200);
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN('USER_DIR','abc.txt','R');
LOOP
BEGIN
UTL_FILE.GET_LINE(F1,V1);
dbms_output.put_line(V1);
EXCEPTION
WHEN no_data_found THEN EXIT;
END;
END LOOP;
IF UTL_FILE.IS_OPEN(F1) THEN
dbms_output.put_line('File is Open');
end if;
UTL_FILE.FCLOSE(F1);
END;
/

Solution 1: sqlloader
create a table for incoming data
define your "loading rules":
Use LOAD DATA
infile '/yourappli/utl_file/abc.txt'
REPLACE
INTO TABLE LOAN_BALANCE_MASTER_INT
fields terminated by '|' optionally enclosed by '"'
(
Col_1,
Col_2,
Col_3
)
Solution 2: Turn each of your delimited string into a collection
Here is how you can loop trough your delimited string V1 in your code, with the cto_table function declared from the documentation link:
DECLARE
V1 VARCHAR2(200);
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN('USER_DIR','abc.txt','R');
Loop
BEGIN
UTL_FILE.GET_LINE(F1,V1);
for x in (
select column_value a from table(cto_table('|', V1) )
) loop
dbms_output.put_line(x.a);
end loop;
EXCEPTION WHEN No_Data_Found
THEN EXIT;
END;
end loop;
IF UTL_FILE.IS_OPEN(F1) THEN
dbms_output.put_line('File is Open');
end if;
UTL_FILE.FCLOSE(F1);
END;
/

Related

How to stop csv from converting string to number in PL/SQL

I have used a PL/SQL code to export data to csv file.The code is similar to below code:
CREATE OR REPLACE PROCEDURE export_to_csv IS
v_file UTL_FILE.file_type;
v_string VARCHAR2(4000);
CURSOR c_emp IS
SELECT empno, ename, deptno, sal, comm
FROM emp;
BEGIN
v_file := UTL_FILE.fopen('CSVDIR', 'empdata.csv', 'w', 1000);
-- if you do not want heading then remove below two lines v_string := 'Emp Code, Emp Name, Dept, Salary, Commission'; UTL_FILE.put_line (v_file, v_string);
FOR cur IN c_emp LOOP
v_string := cur.empno || ',' || cur.ename || ',' || cur.deptno || ',' ||cur.sal || ',' || cur.comm;
UTL_FILE.put_line(v_file, v_string);
END LOOP;
UTL_FILE.fclose(v_file);
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.is_open(v_file) THEN
UTL_FILE.fclose(v_file);
END IF;
END;
/
But one column which datatype is varchar2 is getting converted to number and hence loss of data is occuring.Could anyone please tell me how to stop the conversion?
There is probably no conversion and/or data loss at all. You can test it if you read the file you created. The result should be the same as you see it when you open it in Notepad++. If it's true that Notepad++ shows all the data then there's nothing lossed. So, create the file and read it afterwards. Compare the data with the result and you will know.
create or replace PROCEDURE read_csv IS
v_file UTL_FILE.file_type;
v_string VARCHAR2(4000);
BEGIN
v_file := UTL_FILE.fopen('CSVDIR', 'empdata.csv', 'r', 1000);
LOOP
UTL_FILE.GET_LINE(v_file, v_string);
DBMS_OUTPUT.PUT_LINE(v_string);
END LOOP;
UTL_FILE.fclose(v_file);
EXCEPTION
WHEN OTHERS THEN
IF UTL_FILE.is_open(v_file) THEN
UTL_FILE.fclose(v_file);
END IF;
END;
--
-- R e s u l t
--
-- anonymous block completed
-- 7369,SMITH,20,800,
-- 7499,ALLEN,30,1600,300
-- 7521,WARD,30,1250,500
-- 7566,JONES,20,2975,
-- 7654,MARTIN,30,1250,1400
-- 7698,BLAKE,30,2850,
-- 7782,CLARK,10,2450,
-- 7788,SCOTT,20,3000,
-- 7839,KING,10,5000,
-- 7844,TURNER,30,1500,0
-- 7876,ADAMS,20,1100,
-- 7900,JAMES,30,950,
-- 7902,FORD,20,3000,
-- 7934,MILLER,10,1300,

How do we split a CLOB (with some lines with more than 32K characters) line by line via PLSQL?

I am trying to split a huge CLOB which contains lines with more than 32K characters.
I tried to use this
SELECT REGEXP_SUBSTR(file_cont, '[^'||chr(10)||']+', 1, LEVEL) AS substr
from data_tab where interface = 'Historical'
CONNECT BY LEVEL <= LENGTH(REGEXP_REPLACE(file_cont, '[^'||chr(10)||']+')) + 1
The table data_tab contains some files with pipe as a separator.
The column file_cont is a clob which contains the file we are interested in.
However, when I try to execute the above query, it looks like there is an infinite loop.
For information, the CLOB contains more than 600 lines.
What I want to do is to split the clob, line by line into distinct CLOB.
Do you know a query that can display this result without falling into an infinite loop?
EDIT : The file's size is 22MB.
Thank you in advance.
I have a special package for split and PCRE regular expressions:
https://github.com/xtender/XT_REGEXP
You can find this function in https://github.com/xtender/XT_REGEXP/blob/master/xt_regexp.pck
/**
* Clob simple split
*/
function clob_split_simple(p_clob in clob,p_delim in varchar2)
return clob_table pipelined is
row clob;
l_b number:=1;
l_e number:=1;
$IF DBMS_DB_VERSION.ver_le_11 $THEN
$ELSE
pragma UDF;
$END
begin
while l_e>0
loop
l_e:=instr(p_clob,p_delim,l_b);
pipe row(substr(p_clob,l_b,case when l_e>0 then l_e-l_b else length(p_clob)+length(p_delim)-l_b end));
l_b:=l_e+length(p_delim);
end loop;
end clob_split_simple;
So you can either use this pipelined function:
select *
from table(xt_regexp.clob_split_simple(:clob,chr(10));
or take this code as an example.
clob_table is just a table of clob:
https://github.com/xtender/XT_REGEXP/blob/master/types.sql
create or replace type clob_table as table of clob;
/
create or replace type date_table as table of date;
/
create or replace type number_table as table of number;
/
create or replace type varchar2_table as table of varchar2(4000);
/
create or replace type xml_table as table of xmltype;
/
Update: fixed a bug with long matches: dbms_lob.substr which returns varchar2, replaced with substr(clob) which return clob.
You can use a PL/SQL function to read the and split the value:
If you have the data type:
CREATE TYPE clob_table AS TABLE OF CLOB;
Then the function:
CREATE FUNCTION split_clob(
p_value IN CLOB,
p_delimiter IN VARCHAR2 DEFAULT ','
) RETURN clob_table PIPELINED
IS
v_start PLS_INTEGER;
v_next PLS_INTEGER;
v_len PLS_INTEGER;
BEGIN
v_start := 1;
LOOP
v_next := DBMS_LOB.INSTR( p_value, p_delimiter, v_start );
v_len := CASE v_next WHEN 0 THEN LENGTH( p_value ) + 1 ELSE v_next END - v_start;
PIPE ROW ( SUBSTR( p_value, v_start, v_len ) );
EXIT WHEN v_next = 0;
v_start := v_next + LENGTH(p_delimiter);
END LOOP;
END;
/
For the sample data:
CREATE TABLE table_name ( value CLOB );
DECLARE
v_value TABLE_NAME.VALUE%TYPE := EMPTY_CLOB();
BEGIN
FOR ch IN 65 .. 68 LOOP
FOR i IN 1 .. 10 LOOP
v_value := v_value || RPAD( CHR(ch), 4000, CHR(ch) );
END LOOP;
IF ch < 68 THEN
v_value := v_value || CHR(10);
END IF;
END LOOP;
INSERT INTO table_name ( value ) VALUES ( v_value );
END;
/
Then the output of:
SELECT SUBSTR( s.column_value, 1, 10 ) AS value,
LENGTH( s.column_value ) AS len
FROM table_name t
CROSS APPLY TABLE( split_clob( t.value, CHR(10) ) ) s
Is:
VALUE
LEN
AAAAAAAAAA
40000
BBBBBBBBBB
40000
CCCCCCCCCC
40000
DDDDDDDDDD
40000
db<>fiddle here

Read file and generate query in plsql oracle 11

I am writing a stored procedure where I have to do following: I want to have a file (any format properties, json, xml) which will have information of which columns I want to extract from my table.
For example: my table has columns A,B,C,D,E, and suppose my file.properties has below information
A=1
B=0
C=1
D=1
F=0
So my generated query should be Select A,C,D from my table;
How can I do this in Oracle 11G?
I think you need this
SQL> set serveroutput on;
SQL> create or replace procedure pr_dynamic_sql( v_result out sys_refcursor ) is
v_outfile utl_file.file_type;
v_path varchar2(100) := 'UTL_FILE_DIR';
-- alias for the directory where your text files generated at OS.
v_row varchar2(100);
v_file varchar2(100);
v_letter varchar2(10);
v_number varchar2(10);
v_sql varchar2(100):= 'select ';
begin
v_file := 'myfile.properties';
v_outfile := utl_file.fopen(v_path, v_file, 'r');
loop
begin
utl_file.get_line(v_outfile,v_row);
v_letter := regexp_substr(v_row,'[^=]');
v_number := substr(regexp_substr(v_row,'[^=]+$'),1,1);
if v_number = '1' then
v_sql := v_sql||v_letter||',';
end if;
exception when no_data_found then exit;
end;
end loop;
utl_file.fclose(v_outfile);
v_sql := rtrim(v_sql,',')||' from mytable';
open v_result for v_sql;
end;
and call
SQL> begin
pr_dynamic_sql(v_result => :v_result);
end;
/
to get results as of cursor type.

regex to remove commas between quotes in Oracle 11.2g

My code is:
set serveroutput on size unlimited;
DECLARE
v_line_unclean VARCHAR2(32767); -- 32767 BYTES
v_line_clean VARCHAR2(32767);
v_clean_val VARCHAR2(32767);
SQLSMT VARCHAR2(32767);
v_line_in_record INTEGER;
pattern varchar2(15) := '("[^"]*"|[^,]+)';
v_name VARCHAR2(50);
v_first_column VARCHAR2(200);
EMP_FILE UTL_FILE.FILE_TYPE;
BEGIN
DBMS_OUTPUT.ENABLE(9000000);
EMP_FILE := UTL_FILE.FOPEN('EGIS_FILE_DIR','TEST.csv','R', 32767); -- open the file from oracle directory
v_line_in_record := 0; --we skip the first line
IF UTL_FILE.IS_OPEN(EMP_FILE) THEN
LOOP
v_line_in_record := v_line_in_record + 1;
--DBMS_OUTPUT.PUT_LINE(v_line_in_record);
BEGIN
UTL_FILE.GET_LINE(EMP_FILE,v_line_unclean);
IF v_line_in_record = 1 THEN-- first record here
DBMS_OUTPUT.PUT_LINE('');
ELSIF v_line_in_record = 2 THEN-- second record here (header)
DBMS_OUTPUT.PUT_LINE('');
DBMS_OUTPUT.PUT_LINE(v_line_unclean);
v_first_column := SUBSTR(v_line_unclean,1,instr(v_line_unclean,',',10,1)-1);
dbms_output.put_line('1st '||REGEXP_SUBSTR(v_line_unclean, '[^,]+', 1, 1));
ELSE -- body records here);
SELECT REPLACE(v_line_unclean,'((\)|^).*?(\(|$))|,', '\1')INTO v_line_clean FROM DUAL;
SQLSMT := 'INSERT INTO SITE_CONFIG_2G VALUES('''||v_line_clean||''')';
EXECUTE IMMEDIATE SQLSMT;
END IF;
COMMIT;
DBMS_OUTPUT.PUT_lINE(SQLSMT);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_lINE('NO DATA FOUND EXCEPTION');
EXIT;
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_lINE('TO MANY ROW EXCEPTION');
EXIT;
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(sqlcode||sqlerrm);
ROLLBACK;
EXIT;
END;--EXCEPTION
END LOOP;
END IF;
UTL_FILE.FCLOSE(EMP_FILE);
END;
Result :
INSERT INTO SITE_CONFIG_2G VALUES('1,gold,benz,2018,1,blue,"34,000,000",6.4,new,CSV')
INSERT INTO SITE_CONFIG_2G VALUES('2,silver,bmw,2016,1,silver,"51,000,000",6.5,New,CSV')
INSERT INTO SITE_CONFIG_2G VALUES('3,bronze,range,2017,1,light-blue,"24,000,000",7.8,New,RVS')
I would like to remove commas between quotes in "24,000,000" to give me "24000000"
Current result is:
3,bronze,range,2017,1,light-blue,"24,000,000",7.8,New,RVS
Expected result is:
3,bronze,range,2017,1,light-blue,"24000000",7.8,New,RVS
can you try this.
select regexp_replace('1,gold,benz,2018,1,blue,"34,000,000",6.4,new,CSV',
'(")([^"|,]+)(,)([^"|,]+)(,)([^"|,]+)(")',
'\1\2\4\6\7') from dual;

Can you help me write a procedure in Oracle to spool data from a table to a CSV file?

I am writing a procedure to create a CSV file with the data in an Oracle table. I used "spool filename;" but an error is coming. Can I use spool in PL/SQL?
I think that there are better ways to implement this on Oracle 10g/11g, but this should work fine on Oracle 9i or higher:
CREATE OR REPLACE PROCEDURE prc_file_mult_column_generate(
p_file_dir VARCHAR2, -- mandatory (Oracle directory name)
p_file_name VARCHAR2, -- mandatory
p_sql_query VARCHAR2, -- Multiple column SQL SELECT statement that needs to be executed and processed
p_delimiter CHAR -- column delimiter
)
AS
l_cursor_handle INTEGER;
l_dummy NUMBER;
l_col_cnt INTEGER;
l_rec_tab DBMS_SQL.DESC_TAB;
l_current_col NUMBER(16);
l_current_line VARCHAR2(2047);
l_column_value VARCHAR2(300);
l_file_handle UTL_FILE.FILE_TYPE;
l_print_text VARCHAR2(100);
l_record_count NUMBER(16) := 0;
BEGIN
/* Open file for append*/
l_file_handle := UTL_FILE.FOPEN(p_file_dir, p_file_name, 'a', 2047); --Append Mode, 2047 chars per line max, possibly increasable
l_cursor_handle := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(l_cursor_handle, p_sql_query, DBMS_SQL.native);
l_dummy := DBMS_SQL.EXECUTE(l_cursor_handle);
/* Output column names and define them for latter retrieval of data */
DBMS_SQL.DESCRIBE_COLUMNS(l_cursor_handle, l_col_cnt, l_rec_tab); -- get column names
/* Append to file column headers */
l_current_col := l_rec_tab.FIRST;
IF (l_current_col IS NOT NULL) THEN
LOOP
DBMS_SQL.DEFINE_COLUMN(l_cursor_handle, l_current_col, l_column_value, 300);
l_print_text := l_rec_tab(l_current_col).col_name || p_delimiter;
UTL_FILE.PUT (l_file_handle, l_print_text);
l_current_col := l_rec_tab.NEXT(l_current_col);
EXIT WHEN (l_current_col IS NULL);
END LOOP;
END IF;
UTL_FILE.PUT_LINE (l_file_handle,' ');
/* Append data for each row */
LOOP
EXIT WHEN DBMS_SQL.FETCH_ROWS(l_cursor_handle) = 0; -- no more rows to be fetched
l_current_line := '';
/* Append data for each column */
FOR l_current_col IN 1..l_col_cnt LOOP
DBMS_SQL.COLUMN_VALUE (l_cursor_handle, l_current_col, l_column_value);
l_print_text := l_column_value || p_delimiter;
l_current_line := l_current_line || l_column_value || p_delimiter;
END LOOP;
l_record_count := l_record_count + 1;
UTL_FILE.PUT_LINE (l_file_handle, l_current_line);
END LOOP;
UTL_FILE.FCLOSE (l_file_handle);
DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
EXCEPTION
WHEN OTHERS THEN
-- Release resources
IF DBMS_SQL.IS_OPEN(l_cursor_handle) THEN
DBMS_SQL.CLOSE_CURSOR(l_cursor_handle);
END IF;
IF UTL_FILE.IS_OPEN (l_file_handle) THEN
UTL_FILE.FCLOSE (l_file_handle);
END IF;
--RAISE ;
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.format_error_stack);
END;
/
spool is a sqlplus command. it cannot be used in pl/sql.
it seems that you have been trying a variety of ways to get oracle to do your formatting and file saving. why not have your program that is calling the proc do this work for you?
If you only need the data in a cvs file you can do this:
create a sql file with the query like this:
set feedback off verify off heading off pagesize 0
select field1 || ',' || field2 ... from table;
quit;
/
then call sqlplus from a terminal like this:
sqlplus -S user/password #file.sql> cvsfile.cvs
No, SPOOL is a SQL Plus command so you would have to do this in SQL Plus:
spool myfile.txt
exec myproc
spool off
You would probably also need to set some values before starting the process e.g.
set pagesize 0 linesize 1000 trimspool on
... to get the correct formatting.
Here are a couple of links you might find helpful:
A PL/SQL Tutorial and SQL*Plus User Guide (11g)
Creating an ascii file: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:459020243348 and http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:68212348056 and http://www.oracle-developer.net/display.php?id=425 .

Resources