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

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,

Related

Oracle modify sys refcursor and return the modified cursor in PL/SQL

Im trying to create a procedure which takes sys refcursor as in out parameter and modifies it based on the logic explained in comments in the below code
TYPE t_params IS
TABLE OF VARCHAR2(32767 CHAR);
/
CREATE OR REPLACE PROCEDURE modify_cursor (
p_cursor IN OUT SYS_REFCURSOR,
p_array_binary IN t_params,
p_values IN t_params
)
/*
p_cursor IN OUT SYS_REFCURSOR
-- contains a single row {empId:123, ename:"king", mgr:"Porter",deptNo:200}
p_array_binary IN t_params
-- contains one binary value corresponding to each column in above cursor ["1","0","1","1"]
p_values IN t_params
-- contains one binary value corresponding to each column in above cursor ["123","king2","new manager","200"]
*/
IS
BEGIN
/*
Based on p_array_binary
if binary value 0 then take cursor should retain value as it is fro corresponding column
if binary value 1 then cusrsor should have the correspondoing column value from p_values
In short, the out cursor should be {empId:123, ename:"king", mgr:"new manager", deptNo:200}
*/
END;
/
Any help in this regard will be highly appreciated.
If you knew the ref cursor structure - it was always four columns of the data types shown - then this would be relatively simple:
CREATE OR REPLACE PROCEDURE modify_cursor (
p_cursor IN OUT SYS_REFCURSOR,
p_array_binary IN t_params,
p_values IN t_params
)
IS
l_empid number;
l_ename varchar2(30);
l_mgr varchar2(30);
l_deptNo number;
BEGIN
-- get original values into local variables
fetch p_cursor into l_empId, l_ename, l_mgr, l_deptNo;
-- re-open cursor using either local variables of p_values depending on p_binary flag
open p_cursor for
select
case when p_array_binary(1) = '1' then to_number(p_values(1)) else l_empId end as empId,
case when p_array_binary(2) = '1' then p_values(2) else l_ename end as ename,
case when p_array_binary(3) = '1' then p_values(3) else l_mgr end as mgr,
case when p_array_binary(4) = '1' then to_number(p_values(4)) else l_deptNo end as deptNo
from dual;
END;
/
Demo using your sample data, via SQL*Plus/SQL Developer/SQLcl bind variables:
var rc refcursor;
begin
open :rc for
select 123 as empId, 'king' as ename, 'Porter' as mgr, 200 as deptNo
from dual;
modify_cursor(:rc, t_params('1', '0', '1', '1'), t_params('123', 'king2', 'new manager', '200'));
end;
/
print rc
EMPID ENAME MGR DEPTNO
---------- -------------------------------- -------------------------------- ----------
123 king new manager 200
db<>fiddle
Since you don't know the structure in advance, you will have to use dynamic SQL, which is bit more complicated. Here's an outline:
CREATE OR REPLACE PROCEDURE modify_cursor (
p_cursor IN OUT SYS_REFCURSOR,
p_array_binary IN t_params,
p_values IN t_params
)
IS
l_c integer;
l_col_cnt integer;
l_desc_t dbms_sql.desc_tab3;
l_varchar2 varchar2(32767 char);
l_values t_params := new t_params();
l_result integer;
BEGIN
-- convert ref cursor to dbms_sql cursor
l_c := dbms_sql.to_cursor_number(rc => p_cursor);
-- analyse the cursor (columns, data types)
dbms_sql.describe_columns3(c => l_c, col_cnt => l_col_cnt, desc_t => l_desc_t);
-- optionally check l_col_cnt matches sise of t_params arguments?
l_values.extend(l_col_cnt);
-- define each column for fetch; here you're treating everything as strings,
-- which will cause issues with some other data types
for i in 1..l_col_cnt loop
dbms_sql.define_column(c => l_c, position => i, column => l_varchar2, column_size => 32767);
end loop;
-- fetch original values - only one row to worry about so no loop
l_result := dbms_sql.fetch_rows(c => l_c);
for i in 1..l_col_cnt loop
-- depending on p_array_binary, set l_values from either fetched data or p_values
if p_array_binary(i) = '1' then
l_values(i) := p_values(i);
else
-- this forces everything to varchar2, which is OK (ish) for your sample data;
-- if you have other data types e.g. dates then you will probably want type-specific
-- handling so you can control the conversions - which affects this, define_column
-- and the final cursor to retrieve the values. But you have the same issue with p_values.
dbms_sql.column_value(c => l_c, position => i, value => l_values(i));
end if;
end loop;
-- finished with original cursor, so close it
dbms_sql.close_cursor(c => l_c);
-- re-open ref cursor using l_values data, with another dynamic SQL statement
l_varchar2 := 'select ';
for i in 1..l_col_cnt loop
if i > 1 then
l_varchar2 := l_varchar2 || ', ';
end if;
if l_desc_t(i).col_type = 2 then
l_varchar2 := l_varchar2 || l_values(i);
else
l_varchar2 := l_varchar2 || '''' || l_values(i) || '''';
end if;
l_varchar2 := l_varchar2 || ' as "' || l_desc_t(i).col_name || '"';
end loop;
l_varchar2 := l_varchar2 || ' from dual';
open p_cursor for l_varchar2;
END;
/
Running exactly the same demo block gives:
EMPID ENAM MGR DEPTNO
---------- ---- ----------- ----------
123 king new manager 200
db<>fiddle
You can add handling for other data types if needed, error handling etc.
Read more about dbms_sql.

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;

read delimited file from pl sql procedure

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;
/

sanitize sql statements for dbms_sql

ı have procedure that reads select statements from a table and execute them. it logs results to another table for reporting purpose.
if something like
insert into some table ...
drop table table_name;
exist in my query table it might cause damage on my db.
here is a basic replacement of my code. how can i protect my data?
Is there a way restrict DBMS_SQL cursors to select statements only?
DECLARE
l_cursor NUMBER DEFAULT DBMS_SQL.OPEN_CURSOR;
l_cols NUMBER DEFAULT 0;
l_desc DBMS_SQL.DESC_TAB;
v_varchar2 VARCHAR2(4000) ;
v_log_data VARCHAR2(4000);
l_status INTEGER;
BEGIN
dbms_sql.parse( l_cursor, 'SELECT employee_id, last_name, to_char(hire_date) hire_date FROM EMPloyees where rownum<5', dbms_sql.native );
l_status := dbms_sql.execute(l_cursor);
DBMS_SQL.DESCRIBE_COLUMNS( l_cursor, l_cols, l_desc );
FOR i IN 1 .. l_cols
LOOP
DBMS_SQL.DEFINE_COLUMN(l_cursor, i, v_varchar2,4000);
END LOOP;
LOOP
EXIT
WHEN ( dbms_sql.fetch_rows(l_cursor) <= 0 );
v_log_data := '';
FOR i IN 1 .. l_cols
LOOP
DBMS_SQL.COLUMN_VALUE(l_cursor, i, v_varchar2);
v_log_data :=v_log_data||l_desc(i).col_name || ':' ||v_varchar2||' ';
END LOOP;
dbms_output.put_line(v_log_data);
END LOOP;
DBMS_SQL.CLOSE_CURSOR(l_cursor);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
DBMS_SQL.CLOSE_CURSOR(l_cursor);
END;

ORACLE how to print text in a new line

Basically its a letter printing in text I'm trying to do via an Oracle PL/SQL Procedure.
My Code below: -
DECLARE
out_file UTL_FILE.file_type; lv_file VARCHAR2 (200) DEFAULT TO_CHAR (SYSDATE, 'yyyymmddhhmiss')||'.txt' ;LVSQL varchar2(4000); lV_TXT varchar2(4000);lv_txt1 varchar2(4000);lv_final_str varchar2(4000); BEGIN out_file := UTL_FILE.fopen ('SPOOL_DIR', lv_file, 'W'); for i in 1..5 loop
begin
select tn,tt into lv_txt,lv_txt1 from
(SELECT rownum r, TNAME tn ,TABTYPE tt FROM tab) where r=j;
lv_final_str:=lV_TXT||chr(9)||lv_txt1||CHR(13);
utl_file.put_line(Out_file,lv_final_str );
end; end loop;
UTL_FILE.fclose (out_file);
END;
I'm able to print the values in next next line by concatenating the lv_final_str with CHR(13) and that I use the *utl_file.put_line(Out_file,lv_final_str );* within the loop
I want to do the same thing by collecting the text in a variable inside the loop.
and writing the value of the variable outside the loop, using the below code.
DECLARE
out_file UTL_FILE.file_type;
lv_file VARCHAR2 (200) DEFAULT TO_CHAR (SYSDATE, 'yyyymmddhhmiss')||'.txt' ;LVSQL varchar2(4000);
lV_TXT varchar2(4000);lv_txt1 varchar2(4000);tmpvar varchar2(4000);lv_build_str varchar2(4000);lv_final_str varchar2(4000);
BEGIN
--SELECT TO_CHAR (SYSDATE, 'yyyymmddhhmiss') INTO lv_file FROM DUAL;
out_file := UTL_FILE.fopen ('SPOOL_DIR', lv_file, 'W');
for i in 1..5 loop
begin
select tn,tt into lv_txt,lv_txt1 from
(SELECT rownum r, TNAME tn ,TABTYPE tt FROM tab) where r=i;
tmpvar:=lV_TXT||chr(9)||lv_txt1||CHR(13);
lv_build_str := lv_build_str || chr(9)||tmpvar;
-- utl_file.put_line(Out_file,lv_final_str );
end;
end loop;
lv_final_str:=lv_build_str;
utl_file.put_line(Out_file,lv_final_str );
UTL_FILE.fclose (out_file);
END;
how to do this, please help.
I used chr(10) as well, it is not printing in new line.
if you observe chr(9) which is used to print tab space is working well.
only next line chr(10) or chr(13) is not working. Why...?
any help..
I'm trying since last 3 days... please help.
It depends on whether you're writing to a file for a Unix environment, or Windows.
Unix uses just a single LF character CHR(10) whereas Windows expects a CR followed by LF CHR(13)||CHR(10).

Resources