Read text file to insert data into Oracle SQL table - sql

I am studying Oracle SQL developer.
What I am doing is reading text file line by line from the folder.
Then Inserting data to the SQL table.
I am able to compile my PROCEDURE, however, it doesn't seem to be inserting the data to file.
Create or Replace PROCEDURE Rfile is
f UTL_FILE.FILE_TYPE;
s VARCHAR2(200);
BEGIN
f := UTL_FILE.FOPEN('C:\Projects\','testdatabinary.txt','R');
IF UTL_FILE.IS_OPEN(f) THEN
LOOP
BEGIN
UTL_FILE.GET_LINE(f,s);
IF s IS NULL THEN
EXIT;
END IF;
INSERT INTO DATAINSERT
(COLUMN1, COLUMN2)
VALUES
(s, 'testdatabinary');
END;
END LOOP;
COMMIT;
END IF;
END;
And I have a table DATAINSERT with two varchar(200) type cols
I am not really sure the reasons that PROCEDURE is not inserting data to table
I just checked error message
Error starting at line 1 in command:
EXEC Rfile
ORA-29280: invalid directory path
ORA-06512: at "SYS.UTL_FILE", line 41
ORA-06512: at "SYS.UTL_FILE", line 478
ORA-06512: at "SYSTEM.RFILE", line 5

Not sure what causing problems. For me its working fine here is my example code
--Reference Site
--https://community.oracle.com/thread/3633577?start=0&tstart=0
set serveroutput on;
CREATE or replace DIRECTORY USER_DIR AS '/home/oracle';
GRANT READ ON DIRECTORY USER_DIR TO PUBLIC;
DECLARE
V1 VARCHAR2(200); --32767
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN('USER_DIR','temp.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;
/
set serveroutput off;

You can't able to enter the path directly to the file open command like this
f := UTL_FILE.FOPEN('C:\Projects\','testdatabinary.txt','R');
Instead of entering path directly, you have to create directory using the below sql query
CREATE or replace DIRECTORY USER_DIR AS 'C:\PROJECTS\';
Then enter the directory name to the file open command. It will be like this
f := UTL_FILE.FOPEN('USER_DIR ','testdatabinary.txt','R');

First procedure trying to load the file from PC into the server whereas in second procedure file is from server to server. UTL_FILE.FOPEN works for server-side PL/SQL. So first procedure wont execute properly... If you want to load a file from PC to server, then you need to user any front end like D#K or VB.

The mistake your making here is using the workstation path, you actually need to be using the directory path.
The quickest way to resolve this, is just to use the WINSCP command.
Use WINSCP to tell you what the directory path is, then simply substitute this new information for the old path you have in your code, and everything should work ok.

This is very conceptual and tricky. which you can find more concepts and details below:
https://ora-data.blogspot.in/2016/11/read-and-writ-text-file-using-UTLFILE.html
Sure will be helpful:
create or replace procedure read_file is
f_line varchar2(2000);
f utl_file.file_type;
f_dir varchar2(250);
fname varchar2(50);
Comma1 varchar(10);
Comma2 varchar(10);
Comma3 varchar(10);
Comma4 varchar(10);
Comma5 varchar(10);
f_empno emp.empno%type;
f_ename emp.ename%type;
f_job emp.job%type;
f_mgr emp.mgr%type;
f_hiredate emp.hiredate%type;
f_sal emp.sal%type;
begin
f_dir := ‘E:\PLSQL’;
fname := ‘input.txt’;
f := utl_file.fopen(‘UTL_FILE_DIR’,fname,’r’); –opening the file using fopen function
loop
begin
utl_file.get_line(f,f_line);
–using a loop continuously get the file’s content using get_line function exception when no_data_found then
exit;
end;
Comma1 := INSTR(f_line, ‘,’ ,1 , 1);
Comma2 := INSTR(f_line, ‘,’ ,1 , 2);
Comma3 := INSTR(f_line, ‘,’ ,1 , 3);
Comma4 := INSTR(f_line, ‘,’ ,1 , 4);
Comma5 := INSTR(f_line, ‘,’ ,1 , 5);
–Each field in the input record is delimited by commas.
–We need to find the location of two commas in the line.
–and use the locations to get the field from the line.
f_empno := to_number(SUBSTR(f_line, 1, Comma1-1));
f_ename := SUBSTR(f_line, Comma1+1, Comma2-Comma1-1);
f_job := SUBSTR(f_line, comma2+1, Comma3-Comma2-1);
f_mgr := to_number(SUBSTR(f_line, comma3+1, Comma4-Comma3-1));
f_hiredate := to_date(SUBSTR(f_line, comma4+1, Comma5-Comma4-1),’dd-mon-yyyy’);
f_sal := to_number(SUBSTR(f_line, comma5+1),’99999′); dbms_output.put_line(f_empno ||’ ‘|| f_ename || ‘ ‘ || f_job || ‘ ‘ || f_mgr ||’ ‘ || f_hiredate||’ ‘|| f_sal);
insert into emp12 VALUES (f_empno,f_ename,f_job,f_mgr,f_hiredate,f_sal);
end loop;
utl_file.fclose(f);
commit;
end;
/

first login with
username:sys as sysdba
password should be the same as used for user 'system'
now enter
SQL> grant execute on UTL_FILE to PUBLIC;
Now login with any user with which you want to create procedure

Related

Handle a very large string in pl/sql script

I am trying to run below code which reads the index definition for table A so that it can be created again after I delete/create that in this script. This script runs fine when the returned value(ddl) is small but in other environments where the value is large with 140K characters in one row this script fails with below mentioned error. Please note that I cannot use spool in this case due to some restrictions. Could someone help on how to resolve this issue or suggest some another approach?
Thanks in advance.
"An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2)."
SET SERVEROUTPUT ON;
DECLARE
my_cursor SYS_REFCURSOR;
TYPE clob_array IS VARRAY(15) OF CLOB;
index_array clob_array := clob_array();
v_clob CLOB;
--index_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
BEGIN
OPEN my_cursor FOR 'select replace(dbms_metadata.get_ddl (''INDEX'', index_name), ''"C",'', '''')
from user_indexes
where table_name = ''A''';
LOOP FETCH my_cursor INTO v_clob;
EXIT WHEN my_cursor%NOTFOUND;
index_array.extend;
index_array(index_array.count) := v_clob;
dbms_output.put_line(index_array(index_array.count));
END LOOP;
CLOSE my_cursor;
END;
/
I simulated this issue you are getting this error because of the dbms_output.put_line which displays the output.Try switching to UTL_FILE at the server side OR Try for any alternatives
By the way, the code can be simplified to:
declare
type clob_array is table of clob;
index_array clob_array := clob_array();
begin
for r in (
select replace(dbms_metadata.get_ddl('INDEX', index_name), '"C",') as index_ddl
from user_indexes
where table_name = 'A'
)
loop
index_array.extend;
index_array(index_array.count) := r.index_ddl;
dbms_output.put_line(substr(index_array(index_array.count), 1, 32767));
end loop;
end;
I used substr() to limit the value passed to dbms_output.put_line to its documented limit. You could probably work around it by splitting the text into smaller chunks, and maybe finding the position of the last blank space before position 32767 in order to avoid splitting a word.
Here's what I came up with:
declare
type clob_array is table of clob;
index_array clob_array := clob_array();
procedure put_line
( p_text clob )
is
max_len constant simple_integer := 32767;
line varchar2(max_len);
remainder clob := p_text;
begin
while dbms_lob.getlength(remainder) > max_len loop
line := dbms_lob.substr(remainder,max_len);
line := substr(line, 1, instr(line, ' ', -1));
remainder := substr(remainder, length(line) +1);
dbms_output.put_line(line);
end loop;
if length(trim(remainder)) > 0 then
dbms_output.put_line(remainder);
end if;
end put_line;
begin
for r in (
select replace(dbms_metadata.get_ddl('INDEX', index_name), '"C",') as index_ddl
from user_indexes
where table_name = 'A'
)
loop
index_array.extend;
index_array(index_array.count) := r.index_ddl;
put_line(index_array(index_array.count));
end loop;
end;

UTL_FILE saving in local machine

Hello,
I wanted to save all the functions,procedures,packages in my local machine. I tried with SPOOL but with spool im not sure to get the filename and save it. So i tried with UTL_FILE.. But the problem is my database is in UNIX and I wanted to save in my windows local machine. Im getting the below mentioned error
Fehlerbericht -
ORA-29283: Ungültiger Dateivorgang
ORA-06512: in "SYS.UTL_FILE", Zeile 536
ORA-29283: Ungültiger Dateivorgang
ORA-06512: in Zeile 24
29283. 00000 - "invalid file operation%s"
*Cause: An attempt was made to read from a file or directory that does
not exist, or file or directory access was denied by the
operating system.
*Action: Verify file and directory access privileges on the file system,
and if reading, verify that the file exists.
And the below is my code.
CREATE DIRECTORY FUNC_DIR AS 'C:\Workspace\BE\DB\Funktionen\';
CREATE DIRECTORY PROC_DIR AS 'C:\Workspace\BE\DB\Prozeduren';
CREATE DIRECTORY PACK_DIR AS 'C:\Workspace\BE\DB\Package\';
CREATE DIRECTORY PACBO_DIR AS 'C:\Workspace\BE\DB\Package_Body';
GRANT READ,WRITE ON DIRECTORY FUNC_DIR TO PUBLIC;
GRANT READ,WRITE ON DIRECTORY PROC_DIR TO PUBLIC;
GRANT READ,WRITE ON DIRECTORY PACK_DIR TO PUBLIC;
GRANT READ,WRITE ON DIRECTORY PACBO_DIR TO PUBLIC;
DECLARE
fileHandler UTL_FILE.FILE_TYPE;
filename VARCHAR2(60);
filetext VARCHAR2(32766);
filetype VARCHAR2(20);
BEGIN
FOR a IN (SELECT distinct name,type INTO filename,filetype
FROM all_source
WHERE type IN
('FUNCTION','PROCEDURE','PACKAGE','PACKAGE_BODY')
AND OWNER='HR')
LOOP
filetype := a.object_type;
filename := a.object_name;
IF (filetype = 'FUNCTION') THEN
fileHandler := UTL_FILE.FOPEN('FUNC_DIR', filename||'.sql', 'W');
ELSIF filetype = 'PROCEDURE' THEN
fileHandler := UTL_FILE.FOPEN('PROC_DIR', filename||'.sql', 'W');
ELSIF filetype = 'PACKAGE' THEN
fileHandler := UTL_FILE.FOPEN('PACK_DIR', filename||'.sql', 'W');
ELSIF filetype = 'PACKAGE_BODY' THEN
fileHandler := UTL_FILE.FOPEN('PACBO_DIR', filename||'.sql', 'W');
FOR b IN (SELECT text INTO filetext FROM all_source WHERE TYPE IN('FUNCTION')AND OWNER = 'HR' AND
NAME = (filename))
LOOP
filetext := b.text;
UTL_FILE.PUTF(fileHandler, filetext);
END LOOP;
UTL_FILE.FCLOSE(fileHandler);
ELSE
DBMS_OUTPUT.PUT_LINE('OBJECT TYPE DOES NOT MATCH');
END IF;
END LOOP;
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'ERROR: Invalid PATH FOR file.');
END;
/
UTL_FILE writes to the database server. To write to your local machine, you would need to mount your directories on the db server. This is generally a bad idea!
Use spool to write to your machine just do:
spool c:\path\to\your\folder\file.name
select ....
You can use dbms_output.put_line in your PLSQL together with SPOOL to get the result. For example like the below.This prints ename, sal in the file_name.txt file.
set serveroutput on
Spool e:\file_name.txt
Begin
for c in (Select ename, sal from emp)
loop
dbms_output.put_line(c.ename || ',' || c.sal);
end loop;
End;
/
spool off
for your example, you can use the below
set serveroutput on
Spool C:\Workspace\BE\DB\Funktionen\Function1.sql
BEGIN
FOR a IN (SELECT distinct name,type
FROM all_source
WHERE type IN
('FUNCTION')
AND OWNER='HR')
LOOP
FOR b IN (SELECT text FROM all_source WHERE TYPE IN('FUNCTION')AND OWNER = 'HR' AND
NAME = (a.name))
LOOP
dbms_output.enable;
dbms_output.put_line(b.text);
END LOOP;
END LOOP;
EXCEPTION
WHEN others THEN
dbms_output.put_line( 'ERROR: Invalid PATH FOR file.'||sqlerrm);
END;
/
spool off
You can generate one master file which has plsql for all the functions individually which you can run for all the functions
set serveroutput on
spool C:\Workspace\BE\DB\Funktionen\function12.sql
begin
FOR a IN (SELECT distinct name,type
FROM all_source
WHERE type IN
('FUNCTION')
AND OWNER='HR')
loop
dbms_output.enable ;
dbms_output.put_line('set serveroutput on
Spool C:\Workspace\BE\DB\Funktionen\'||a.name||'.sql
BEGIN
FOR a1 IN (SELECT distinct name,type
FROM all_source
WHERE type IN
(''FUNCTION'')
AND OWNER=''HR'' and name='''||a.name||''')
LOOP
FOR b IN (SELECT text FROM all_source WHERE TYPE IN(''FUNCTION'')AND OWNER = ''HR'' AND
NAME = '''||a.name||'''))
LOOP
dbms_output.enable;
dbms_output.put_line(b.text);
END LOOP;
END LOOP;
EXCEPTION
WHEN others THEN
dbms_output.put_line( ''ERROR: Invalid PATH FOR file.''||sqlerrm);
END;'
);
END LOOP;
END;
/
spool off

How to load csv files into a table

Good Morning, I've been trying for some days to load data from a csv file into a table, BUT I found out how to do it if you have privileges to create a new directory objects.
I did it locally with both ways UTL_FILE and external tables, but, I need special privileges to do it.
But when I tried to do it online I found out that I haven't got those privileges so, I'd like to find out if there's any way to do it without them.
Thank you!
PS: The two approaches are as follows:
CREATE TABLE TEST
(
A varchar2(25);
B number;
)
WITH UTL_FILE:
PROCEDURE load AS
F UTL_FILE.FILE_TYPE;
V_LINE VARCHAR2(4000);
v_A varchar2(25);
v_B number;
BEGIN
F := UTL_FILE.FOPEN (<directory>, '<csv file>', 'r');
IF UTL_FILE.IS_OPEN(F) THEN
LOOP
BEGIN
UTL_FILE.GET_LINE(F, V_LINE, 1000);
IF V_LINE IS NULL THEN
EXIT;
END IF;
v_A := REGEXP_SUBSTR(V_LINE, '[^;]+', 1, 1);
v_B := REGEXP_SUBSTR(V_LINE, '[^;]+', 1, 2);
INSERT INTO test(v_A, v_B);
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
EXIT;
END;
END LOOP;
END IF;
UTL_FILE.FCLOSE(F);
END load;
WITH external tables
PROCEDURE loadcsv AS
A varchar2(25);
B number;
BEGIN
INSERT INTO test select * testload;
COMMIT;
END loadcsv;
HAVING
CREATE TABLE testload
(
A VARCHAR2(25)
B number
)
ORGANIZATION EXTERNAL
( TYPE ORACLE_LOADER
DEFAULT DIRECTORY "PRUEBAS"
ACCESS PARAMETERS
( records delimited by newline
fields terminated by ';'
)
LOCATION
( 'Log_CSV_REND_SICTG_20170507.csv'
)
)
REJECT LIMIT UNLIMITED ;
You might want to try loading with SQL Loader. It is easy to use and very fast. You have to create a control file that describes how the data will be loaded, and needs to have the same structure as the table you're trying to load into. For you case it might look something like this.
test.ctl
LOAD DATA
INFILE <yourfilepath>
APPEND INTO TABLE TEST
FIELDS TERMINATED BY ','
(
A,
B
)
Then, from the command line.
host sqlldr username/password control=youfolder\test.ctl
log=yourfolder\test.log

dbms_xmldom - creating an xml from an oracle database

I am trying to create an xml file from an oracle database.
I am not able to get the correct format and was wondering if I could get some assistance.
This is part of the script:
l_record_element := dbms_xmldom.createElement(l_domdoc, 'record_type');
dbms_xmldom.setAttribute(l_record_element,'desc', r_dept.public1);
l_record_node := dbms_xmldom.appendChild(l_dept_node,dbms_xmldom.makeNode(l_record_element));
my output:
<record_type desc="Public"/>
The output I need:
<record_type desc="Public">PUBLIC</record_type>
Thanks!
You need to create and append a text node with the PUBIC value.
Demo showing that coming from the same cursor that provides the record type:
set serveroutput on
declare
l_domdoc dbms_xmldom.domdocument;
l_dept_node dbms_xmldom.domnode;
l_record_node dbms_xmldom.domnode;
l_record_element dbms_xmldom.domelement;
l_record_text dbms_xmldom.domtext;
l_tmp_node dbms_xmldom.domnode;
l_xmltype xmltype;
l_buffer varchar2(32767);
begin
l_domdoc := dbms_xmldom.newDOMDocument; --(xmltype('<data />'));
for r_dept in (select 'Public' as public1, 'PUBLIC' as public2 from dual) loop
l_dept_node := dbms_xmldom.makeNode(l_domdoc);
-- code you showed
l_record_element := dbms_xmldom.createElement(l_domdoc, 'record_type');
dbms_xmldom.setAttribute(l_record_element,'desc', r_dept.public1);
l_record_node := dbms_xmldom.appendChild(l_dept_node, dbms_xmldom.makeNode(l_record_element));
-- add a text node
l_record_text := dbms_xmldom.createTextNode(l_domdoc, r_dept.public2);
l_tmp_node := dbms_xmldom.appendChild(l_record_node, dbms_xmldom.makeNode(l_record_text));
-- display the node for demo
l_xmltype := dbms_xmldom.getXmlType(l_domdoc);
dbms_xmldom.freeDocument(l_domdoc);
dbms_output.put_line(l_xmltype.getClobVal);
end loop;
end;
/
<record_type desc="Public">PUBLIC</record_type>
PL/SQL procedure successfully completed.

ORACLE BLOB to FILE

I am writing some pl/sql to generate pdf reports that are stored as blobs in an oracle table. I need to loop through this table which has a column for filename and blob and write the blob to the OS as a file with the corresponding filename in the table. I pretty much have completed this code but am running into a snag:
ORA-06550: line 13, column 59:
PL/SQL: ORA-00904: "SIMS_PROD"."PUBLISH_RPT_NEW"."RPT_FILE_NAME": invalid identifier
ORA-06550: line 13, column 12:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
Cause: Usually a PL/SQL compilation error.
Action:
I did read the post on the site: How can I extract files from an Oracle BLOB field? - however - this is only for one file - my table contains hundreds of rows each that has a blob and associated filename - its the looping through this table thats giving me grief.
I need to prefix the schema name, table and column explicitly since I am logged in as a DBA user and not as the owner of the schema itself. Here is my code - what am I missing here or doing wrong. Thanks in advance for any help from the community - its much appreciated.
DECLARE
t_blob BLOB;
t_len NUMBER;
t_file_name VARCHAR2(100);
t_output utl_file.file_type;
t_totalsize NUMBER;
t_position NUMBER := 1;
t_chucklen NUMBER := 4096;
t_chuck RAW(4096);
t_remain NUMBER;
BEGIN
FOR X IN (SELECT SIMS_PROD.publish_rpt_new.RPT_FILE_NAME, SIMS_PROD.publish_rpt_new.RPT_CONTENTS FROM SIMS_PROD.PUBLISH_RPT)
LOOP
-- Get length of blob
SELECT dbms_lob.Getlength (SIMS_PROD.publish_rpt_new.RPT_CONTENTS), SIMS_PROD.publish_rpt_new.RPT_FILE_NAME INTO t_totalsize, t_file_name FROM SIMS_PROD.publish_rpt_new;
t_remain := t_totalsize;
-- The directory TEMPDIR should exist before executing
t_output := utl_file.Fopen ('PDF_REP', t_file_name, 'wb', 32760);
-- Get BLOB
SELECT SIMS_PROD.publish_rpt_new.RPT_CONTENTS INTO t_blob FROM SIMS_PROD.publish_rpt_new;
-- Retrieving BLOB
WHILE t_position < t_totalsize
LOOP
dbms_lob.READ (t_blob, t_chucklen, t_position, t_chuck);
utl_file.Put_raw (t_output, t_chuck);
utl_file.Fflush (t_output);
t_position := t_position + t_chucklen;
t_remain := t_remain - t_chucklen;
IF t_remain < 4096 THEN t_chucklen := t_remain;
END IF;
END LOOP;
END LOOP;
END;
Try replacing the line 13 with this:
FOR X IN (SELECT RPT_FILE_NAME, RPT_CONTENTS FROM SIMS_PROD.PUBLISH_RPT)
It is going to be a too late answer but at least you will know where have you done mistake. The problem is if you use:
FOR X IN (SELECT a, b from table) LOOP
You have to use X.a in next statements of this loop to properly refer to values of selected rows.
So in your code you had to change SIMS_PROD.publish_rpt_new.RPT_CONTENTS to X.SIMS_PROD.publish_rpt_new.RPT_CONTENTS.
I haven't read the rest of your code, so maybe there could be some more mistakes too.
have done a similar thing recently,
ping me for exact additional details
but the gist is this
create/frame excel or pdf as blob and store in to a blob column
use a base 64 converter function to store the same data into clob ( as text )
use sqlplus from windows/linux to spool the text from the clob column
convert the clob to blob with desired filename using os tools (probably ssl/certificate has utility to convert b64 t0 binary back )
Maybe you could consider writing a procedure separetly to save a BLOB as a file (any BLOB from any table) and then just loop through your table passing a BLOB, directory and file name. This way it could be used independently.
Here is the function (it is a function for some other reasons - you can change it to procedure) that does it like described:
Function BLOB2FILE (mBLOB BLOB, mDir VARCHAR2, mFile VARCHAR2) RETURN VarChar2
IS
BEGIN
Declare
utlFile UTL_FILE.FILE_TYPE;
utlBuffer RAW(32767);
utlAmount BINARY_INTEGER := 32767;
utlPos INTEGER := 1;
utlBlobLen INTEGER;
mRet VarChar2(100);
Begin
utlBlobLen := DBMS_LOB.GetLength(mBLOB);
utlFile := UTL_FILE.FOPEN(mDir, mFile,'wb', 32767);
--
WHILE utlPos <= utlBlobLen LOOP
DBMS_LOB.READ(mBLOB, utlAmount, utlPos, utlBuffer);
UTL_FILE.PUT_RAW(utlFile, utlBuffer, TRUE);
utlPos := utlPos + utlAmount;
END LOOP;
--
UTL_FILE.FCLOSE(utlFile);
mRet := 'OK - file created' || mFile;
RETURN mRet;
Exception
WHEN OTHERS THEN
IF UTL_FILE.IS_OPEN(utlFile) THEN
UTL_FILE.FCLOSE(utlFile);
END IF;
mRet := 'ERR - CLOB_ETL.BLOB2FILE error message ' || Chr(10) || SQLERRM;
RETURN mRet;
End;
END BLOB2FILE;
If you can select your BLOBS from any table just loop and pass it to the function/procedure with the directory and file name...