How can I populate the columns with data from a text file, where each line is a different column? The text file looks like this:
Amsterdam Aalsmeerweg
Aalsmeerweg
14
Amsterdam
NL
1059NJ
0204122131
Amsterdam Bos en Lommerweg
Bos en Lommerweg
215
AMSTERDAM
NL
1055DT
0206847676
Amsterdam Ceintuurbaan
Ceintuurbaan
314
AMSTERDAM
NL
1072GL
0204705292
This post didn't help me.
My code looks like this:
set serveroutput on;
CREATE or replace DIRECTORY USER_DIR AS '/home/renejanssen/';
GRANT READ ON DIRECTORY USER_DIR TO PUBLIC;
declare
V1 VARCHAR2(200);
F1 UTL_FILE.FILE_TYPE;
f_name WINKEL.NAAM%type;
f_adres varchar2(255);
f_homenr WINKEL.HUISNR%TYPE;
f_city varchar2(100);
f_countrcode varchar(255);
f_zipcode WINKEL.POSTCODE%TYPE;
f_phonenr WINKEL.TELNR%TYPE;
v_counter number DEFAULT 1; --counter for ID
BEGIN
F1 := UTL_FILE.FOPEN('USER_DIR','test','R');
Loop
BEGIN
UTL_FILE.GET_LINE(F1,V1);
dbms_output.put_line(v_counter || ' = ' || V1);
EXCEPTION WHEN No_Data_Found THEN EXIT;
dbms_output.put_line('niets');
END;
f_name := substr(V1,1);
f_adres := substr(V1,1);
f_homenr := substr(V1,1);
f_countrcode := substr(V1,1);
f_zipcode := substr(V1,1);
insert INTO WINKEL (ID, NAAM, HUISNR, POSTCODE, TELNR )
values(v_counter, f_name, f_homenr, f_zipcode, f_phonenr);
v_counter := v_counter +1;
end loop;
IF UTL_FILE.IS_OPEN(F1) THEN
dbms_output.put_line('File is Open');
end if;
UTL_FILE.FCLOSE(F1);
END;
If each entry in the file always has seven lines as you've shown, followed by a blank line, you can do seven reads directly into your PL/SQL variables:
declare
v1 varchar2(200);
f1 utl_file.file_type;
f_name winkel.naam%type;
f_adres varchar2(255);
f_homenr winkel.huisnr%type;
f_city varchar2(100);
f_countrcode varchar(255);
f_zipcode winkel.postcode%type;
f_phonenr winkel.telnr%type;
v_counter number default 1; --counter for ID
begin
f1 := utl_file.fopen('D42','test','R');
loop
begin
utl_file.get_line(f1, f_name);
utl_file.get_line(f1, f_adres);
utl_file.get_line(f1, f_homenr);
utl_file.get_line(f1, f_city);
utl_file.get_line(f1, f_countrcode);
utl_file.get_line(f1, f_zipcode);
utl_file.get_line(f1, f_phonenr);
exception
when no_data_found then
exit;
dbms_output.put_line('niets');
end;
insert into winkel(id, naam, huisnr, postcode, telnr)
values (v_counter, f_name, f_homenr, f_zipcode, f_phonenr);
v_counter := v_counter +1;
begin
-- consume possible blank line; don't error as final entry may not have one
utl_file.get_line(f1, f_adres);
dbms_output.put_line('extra line: ' || f_adres);
exception
when no_data_found then
dbms_output.put_line('extra line: ' || sqlerrm);
null;
end;
end loop;
if utl_file.is_open(f1) then
dbms_output.put_line('File is Open');
end if;
utl_file.fclose(f1);
end;
/
With your data file that populates a table with:
select * from winkel;
ID NAAM HUISNR POSTCODE TELNR
---------- ---------------------------------------- ---------- ---------- --------------
1 Amsterdam Aalsmeerweg 14 1059NJ 0204122131
2 Amsterdam Bos en Lommerweg 215 1055DT 0206847676
3 Amsterdam Ceintuurbaan 314 1072GL 0204705292
Related
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.
I have a procedure which receive as input parameter a record with 170 columns (it is based on the structure of a table).
In the procedure I want to call a debugging procedure one of whose parameters is a text string containing all the field names and values of this record.
For example:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN table_name%ROWTYPE) as
text VARCHAR2(10000) := NULL;
BEGIN
...
text := 'pi_record.column1 = ' || pi_record.column1 || CHR(13) ||
'pi_record.column2 = ' || pi_record.column2 || CHR(13) ||
...
'pi_record.column170 = ' || pi_record.column170;
logging_procedure (text);
...
END;
Is there any simple way to achieve this in a dynamic way (looping through record fields names and values) without enumerating all of them?
Maybe something like this:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN table_name%ROWTYPE) as
text VARCHAR2(10000) := NULL;
BEGIN
...
LOOP in pi_record.columns
text := text || CHR(13) || pi_record.column.name || ' : ' || pi_record.column.value
END LOOP
logging_procedure (text);
...
END;
Many thanks,
Here's one way to do that. A package spec contains a variable whose type matches the one we'll use in a procedure.
SQL> set serveroutput on
SQL> create or replace package pkg_xxx
2 as
3 dept_rec dept%rowtype;
4 end;
5 /
Package created.
SQL> create or replace procedure xxx (pi_record in dept%rowtype)
2 as
3 text varchar2 (10000) := null;
4 l_str varchar2 (200);
5 l_var varchar2 (200);
6 begin
7 pkg_xxx.dept_rec := pi_record;
8
9 for cur_r in ( select column_name
10 from user_tab_columns
11 where table_name = 'DEPT'
12 order by column_id)
13 loop
14 l_str :=
15 'begin '
16 || ':x := to_char(pkg_xxx.dept_rec.'
17 || cur_r.column_name
18 || '); '
19 || 'end; ';
20
21 execute immediate l_str using out l_var;
22
23 text := text || chr (10) || cur_r.column_name || ' = ' || l_var;
24 end loop;
25
26 dbms_output.put_line (text);
27 end;
28 /
Procedure created.
Now, let's pass something to the procedure and see what happens:
SQL> declare
2 cursor c1
3 is
4 select *
5 from dept
6 where deptno = 10;
7
8 c1r c1%rowtype;
9 begin
10 open c1;
11 fetch c1 into c1r;
12 close c1;
13
14 xxx (c1r);
15 end;
16 /
DEPTNO = 10
DNAME = ACCOUNTING
LOC = NEW YORK
PL/SQL procedure successfully completed.
SQL>
Huh, kind of works (if that's what you asked). Of course, it is just an example, you'll have to modify it if you want to get something really smart (hint: DATE columns).
The only idea I have is to insert the record into a TEMP table:
CREATE OR REPLACE PROCEDURE xxx (pi_record IN TABLE_NAME%ROWTYPE) AS
TEXT VARCHAR2(10000) := NULL;
item VARCHAR2(1000);
TABLE_DOES_NOT_EXIST EXCEPTION;
PRAGMA EXCEPTION_INIT(TABLE_DOES_NOT_EXIST, -942);
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE TABLE_NAME_TMP';
EXCEPTION
WHEN TABLE_DOES_NOT_EXIST then null;
END;
EXECUTE IMMEDIATE 'CREATE GLOBAL TEMPORARY TABLE TABLE_NAME_TMP AS SELECT * FROM TABLE_NAME WHERE ROWNUM = 0';
DELETE FROM TABLE_NAME_TMP;
INSERT INTO TABLE_NAME_TMP VALUES pi_record;
FOR aCol IN (SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE table_name = 'TABLE_NAME' ORDER BY COLUMN_ID) LOOP
EXECUTE IMMEDIATE 'SELECT '||aCol.COLUMN_NAME||' FROM TABLE_NAME_TMP' INTO item;
TEXT := TEXT || CHR(13) || aCol.COLUMN_NAME || ' : ' || item;
END LOOP;
DBMS_OUTPUT.PUT_LINE ( TEXT );
END;
In case table TABLE_NAME has static attributes then you should skip dynamic DROP TABLE ... and CREATE GLOBAL TEMPORARY TABLE ... and create the TEMP table only once.
everyone!
I got a different approach to get the difference between records dynamically:
You just have to create the global variables on the package header as bellow:
v_NAME_OF_TABLE_new NAME_OF_TABLE%rowtype;
v_NAME_OF_TABLE_old NAME_OF_TABLE%rowtype;
then create the function on your pkg body that return a boolean even if a field is different:
function is_different(p_old NAME_OF_TABLE%rowtype, p_new NAME_OF_TABLE%rowtype)
return boolean
is
cursor cols is
select tb.COLUMN_NAME
from all_tab_columns tb
where tb.OWNER = 'DW'
and tb.TABLE_NAME = 'NAME_OF_TABLE'
order by tb.COLUMN_ID;
l_sql varchar2(4000);
l_new varchar2(4000);
l_old varchar2(4000);
begin
pkg_NAME.v_NAME_OF_TABLE_new := p_new;
pkg_NAME.v_NAME_OF_TABLE_old := p_old;
for reg in cols loop
l_sql := '
begin
:x := pkg_NAME.v_NAME_OF_TABLE_new.'||reg.COLUMN_NAME||';'||'
end;';
execute immediate l_sql using out l_new;
l_sql := '
begin
:x := pkg_NAME.v_NAME_OF_TABLE_old.'||reg.COLUMN_NAME||';'||'
end;';
execute immediate l_sql using out l_old;
--- dbms_output.put_line(l_new||' - '||l_old);
if nvl(l_new,'NULO') <> nvl(l_old,'NULO') then
return true;
end if;
end loop;
return false;
end;
Atention: This can turn your process heavier and slower.
That's all!
Hope this can be helpful!
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;
I am using Oracle 12, and I want to make a dynamic procedure which selects rows from specific table but according to an unknown conditio. That condition will be specified as input parameter.
Suppose I have a column called employee id and I want to call the procedure
with the following condition
execute s('employeeid = 2')
My code is
create or replace procedure s (condition varchar)
as
TYPE EmpCurTyp IS REF CURSOR; -- define weak REF CURSOR type
emp_cv EmpCurTyp; -- declare cursor variable
my_ename VARCHAR2(15);
my_sal NUMBER := 2;
mycondition varchar2(100):=condition;
BEGIN
OPEN emp_cv FOR -- open cursor variable
'SELECT employeeid, employeename FROM employees WHERE = :s' USING mycondition;
END;
but I am getting an error
missing expression
What am I doing wrong, and will the result of this procedure be selected rows from employees table that satisfy applied condition ?
The USING is meant to handle values, not pieces of code; if you need to edit your query depending on an input parameter ( and I believe this is a very dangerous way of coding), you should treat the condition as a string to concatenate to the query.
For example, say you have this table:
create table someTable(column1 number)
This procedure does somthing similar to what you need:
create or replace procedure testDyn( condition IN varchar2) is
cur sys_refcursor;
begin
open cur for 'select column1 from sometable where ' || condition;
/* your code */
end;
Hot it works:
SQL> exec testDyn('column1 is null');
PL/SQL procedure successfully completed.
SQL> exec testDyn('column99 is null');
BEGIN testDyn('column99 is null'); END;
*
ERROR at line 1:
ORA-00904: "COLUMN99": invalid identifier
ORA-06512: at "ALEK.TESTDYN", line 4
ORA-06512: at line 1
This is not embedded in a procedure yet but I tested this and works:
DECLARE
TYPE OUT_TYPE IS TABLE OF VARCHAR2 (20)
INDEX BY BINARY_INTEGER;
l_cursor INTEGER;
l_fetched_rows INTEGER;
l_sql_string VARCHAR2 (250);
l_where_clause VARCHAR2 (100);
l_employeeid VARCHAR2 (20);
l_employeename VARCHAR2 (20);
l_result INTEGER;
o_employeeid OUT_TYPE;
o_employeename OUT_TYPE;
BEGIN
l_cursor := DBMS_SQL.OPEN_CURSOR;
l_sql_string := 'SELECT employeeid, employeename FROM employees WHERE ';
l_where_clause := 'employeeid = 2';
l_sql_string := l_sql_string || l_where_clause;
DBMS_SQL.PARSE (l_cursor, l_sql_string, DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
1,
l_employeeid,
20);
DBMS_SQL.DEFINE_COLUMN (l_cursor,
2,
l_employeename,
20);
l_fetched_rows := 0;
l_result := DBMS_SQL.EXECUTE_AND_FETCH (l_cursor);
LOOP
EXIT WHEN l_result = 0;
DBMS_SQL.COLUMN_VALUE (l_cursor, 1, l_employeeid);
DBMS_SQL.COLUMN_VALUE (l_cursor, 2, l_employeename);
l_fetched_rows := l_fetched_rows + 1;
o_employeeid (l_fetched_rows) := l_employeeid;
o_employeename (l_fetched_rows) := l_employeename;
l_result := DBMS_SQL.FETCH_ROWS (l_cursor);
END LOOP;
DBMS_SQL.CLOSE_CURSOR (l_cursor);
DBMS_OUTPUT.PUT_LINE (o_employeeid (1));
DBMS_OUTPUT.PUT_LINE (o_employeename (1));
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE ('GENERAL FAILURE: ' || SQLERRM);
END;
I recently created a PL/SQL program that creates five different pipe delimited files from related data in a database.
I could not find a way to dynamically pull different tabular data in this case cursors, into a generic procedure that would create the files.
Instead I had to create five separate procedures, one for each file, that took in five different cursors, one for each file requirement record selection.
I can't help but think that there has to be a better way. I was looking into reference cursors but I don't think they are exactly what I am looking for.
How can I achieve this in PL/SQL?
I think what I am looking for is some generic type that can take any data from a cursor given any amount of records and record columns and have the ability to query itself to find what data is in it.
Pass the cursor into your procedure as a SYS_REFCURSOR. Then, use DBMS_SQL.TO_CURSOR_NUMBER(); to convert the ref cursor to a DBMS_SQL cursor.
Then, use DBMS_SQL.DESCRIBE_COLUMNS to figure out the columns in the cursor and DBMS_SQL.DEFINE_COLUMN, DBMS_SQL.FETCH_ROWS and DBMS_SQL.VALUE to get the data from the cursor into PL/SQL variables. Then, write your PL/SQL variables to your output file.
Here's some code that puts all that together for you.
DECLARE
l_rc SYS_REFCURSOR;
PROCEDURE dump_cursor (p_rc IN OUT SYS_REFCURSOR) IS
-- Dump the results of p_rc to log
l_cursor INTEGER;
l_column_count INTEGER;
l_column_descriptions SYS.DBMS_SQL.desc_tab;
l_status INTEGER;
l_column_value VARCHAR2 (4000);
l_column_width NUMBER;
l_rec_count NUMBER := 0;
l_line VARCHAR2 (4000);
FUNCTION get_length (l_column_def IN SYS.DBMS_SQL.desc_rec)
RETURN NUMBER IS
l_width NUMBER;
BEGIN
l_width := l_column_def.col_max_len;
l_width := CASE l_column_def.col_type WHEN 12 THEN /* DATE */
20 WHEN 2 THEN /* NUMBER */
10 ELSE l_width END;
-- Don't display more than 256 characters of any one column (this was my requirement -- your file writer probably doesn't need to do this
l_width := LEAST (256, GREATEST (l_width, l_column_def.col_name_len));
RETURN l_width;
END get_length;
BEGIN
-- This is the date format that I want to use for dates in my output
EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY HH24:MI:SS''';
l_cursor := sys.DBMS_SQL.to_cursor_number (p_rc);
-- Describe columns
sys.DBMS_SQL.describe_columns (c => l_cursor, col_cnt => l_column_count, desc_t => l_column_descriptions);
l_line := '';
FOR i IN 1 .. l_column_count LOOP
l_column_width := get_length (l_column_descriptions (i));
l_line := l_line || RPAD (l_column_descriptions (i).col_name, l_column_width);
l_line := l_line || ' ';
DBMS_SQL.define_column (l_cursor,
i,
l_column_value,
4000);
END LOOP;
DBMS_OUTPUT.put_line (l_line);
l_line := '';
FOR i IN 1 .. l_column_count LOOP
l_column_width := get_length (l_column_descriptions (i));
l_line := l_line || RPAD ('-', l_column_width, '-');
l_line := l_line || ' ';
DBMS_SQL.define_column (l_cursor,
i,
l_column_value,
4000);
END LOOP;
DBMS_OUTPUT.put_line (l_line);
-- l_status := sys.DBMS_SQL.execute (l_cursor);
WHILE (sys.DBMS_SQL.fetch_rows (l_cursor) > 0) LOOP
l_rec_count := l_rec_count + 1;
l_line := '';
FOR i IN 1 .. l_column_count LOOP
DBMS_SQL.COLUMN_VALUE (l_cursor, i, l_column_value);
l_column_value := TRANSLATE (l_column_value, CHR (10), CHR (200));
l_column_width := get_length (l_column_descriptions (i));
IF l_column_value IS NULL THEN
l_line := l_line || RPAD (' ', l_column_width);
ELSE
l_line := l_line || RPAD (l_column_value, l_column_width);
END IF;
l_line := l_line || ' ';
END LOOP;
DBMS_OUTPUT.put_line (l_line);
END LOOP;
IF l_rec_count = 0 THEN
DBMS_OUTPUT.put_line ('No data found.');
ELSE
DBMS_OUTPUT.put_line (l_rec_count || ' rows returned.');
END IF;
sys.DBMS_SQL.close_cursor (l_cursor);
-- It would be better to store the current NLS_DATE_FORMAT on entry and restore it here, instead of assuming that it was
-- set to DD-MON-YYYY.
EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY''';
EXCEPTION
WHEN OTHERS THEN
EXECUTE IMMEDIATE 'alter session set nls_date_format=''DD-MON-YYYY''';
-- Add your own handling here.
END dump_cursor;
-- Tester code, make sure server output is on
BEGIN
OPEN l_rc FOR 'SELECT object_id, object_name, object_type FROM dba_objects WHERE rownum <= 15';
dump_cursor(l_rc);
END;