我在 Oracle 中使用 UTL_FILE 创建了 CSV 文件。我如何读回它?
I created CSV file using UTL_FILE in Oracle. How do I read it back?
我编写了一个程序来将视图内容提取到 Oracle 中的文件系统中。
现在客户想要测试文件的内容。
我如何在 Oracle 中以通用方式执行此操作?
程序:
CREATE OR REPLACE PROCEDURE PRC_EXTRACT_VIEW (p_view_name VARCHAR2, p_timestamp VARCHAR2)
AUTHID CURRENT_USER
IS
/* Run:
set serveroutput on timing on
begin PRC_EXTRACT_VIEW('V_TEST'); end;
*/
p_query VARCHAR2(512) :='SELECT * FROM '||p_view_name;
p_separator VARCHAR2(8) := '';
p_dir_obj VARCHAR2(126) :='TEST_DIR';
p_filename VARCHAR2(128) :=p_view_name||'_'||p_timestamp||'.csv';
l_output utl_file.file_type;
l_cnt NUMBER := 0;
l_theCursor INTEGER DEFAULT dbms_sql.open_cursor;
l_columnValue VARCHAR2(2000):='';
l_status INTEGER;
l_separator VARCHAR2(10) DEFAULT '';
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
v_sql dbms_sql.varchar2a;
v_sql_1 varchar2(32767);
pn varchar2(32):='PRC_EXTRACT_VIEW';
v_ErrMsg varchar2(2000);
v_Errcode varchar2(60);
v_etl_btch_id number;
v_filename varchar2(256):=p_filename;
BEGIN
l_output := utl_file.fopen( p_dir_obj, v_filename, 'w',32767 );
DBMS_SQL.PARSE(l_theCursor, p_query, DBMS_SQL.NATIVE);
FOR i IN 1 .. 255
LOOP
BEGIN
dbms_sql.define_column( l_theCursor, i, l_columnValue, 2000 );
EXCEPTION
WHEN OTHERS THEN
IF ( SQLCODE = -1007 ) THEN
EXIT;
ELSE
raise;
END IF;
END;
END LOOP;
dbms_sql.define_column( l_theCursor, 1, l_columnValue, 2000 );
d := DBMS_SQL.EXECUTE(l_theCursor);
DBMS_SQL.DESCRIBE_COLUMNS(l_theCursor, col_cnt, rec_tab);
LOOP
EXIT
WHEN ( dbms_sql.fetch_rows(l_theCursor) <= 0 );
l_separator := '';
FOR i IN 1 .. col_cnt
LOOP
dbms_sql.column_value( l_theCursor, i, l_columnValue );
--dbms_output.put_line(l_columnValue);
utl_file.put( l_output, l_separator || l_columnValue );
l_separator := p_separator;
END LOOP;
utl_file.new_line( l_output );
l_cnt := l_cnt+1;
END LOOP;
dbms_sql.close_cursor(l_theCursor);
utl_file.fclose( l_output );
/*--TODO
EXCEPTION
WHEN OTHERS THEN
*/
END PRC_EXTRACT_VIEW;
/
客户对文件中的内容有些醉意。
有没有办法在同一个会话或新会话中读取转储文件的内容?
(用于测试目的)
您可以根据您用于假脱机的视图生成外部 table。
对于相同的 p_view_name
你做一次然后即使 p_timestamp
改变新的摘录你也可以改变现有的永恒 table:
alter table ext_table location ('new_filename');
如果p_view_name
改变了那么你必须重新运行table生成脚本。
我提供的解决方案只有在数据文件中的每一列的所有行都具有固定长度时才有效。
首先,您必须生成一个 "column list view" - 包含所有声明的列名称和源视图长度的扁平化结构。
/* Execute:
sqlplus scott/tiger@orcl1 @gen_column_list_view_ddl.sql
*/
SET SQLBLANKLINES ON
SET PAGESIZE 0 FEEDBACK OFF
SET TRIMSPOOL ON LINE 32000
SET WRAP ON
SET NEWPAGE NONE
SET PAGESIZE 0
SET SPACE 0 LONG 1000
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
set termout off
spool GENERATE_EXTERNAL_TABLE/GENERATED/v_column_list.sql
PROMPT set sqlblanklines on
select '
create or replace view v_column_list
as select ' from dual
union all
select ''''||column_name||''' '||column_name||', length('||column_name||') "'||column_name||'_len"'||(case cid when max_cid then '' else ',' end) len
from
(select column_id cid, max(column_id) over() max_cid,column_name
from all_tab_columns
where table_name=:source_view order by column_id)
union all
select '
from '||:source_view||'
where rownum <2' from dual;
PROMPT /
--PROMPT exit;;
spool off
因为您在没有分隔符的情况下提取,这意味着它的长度是固定的,并且给定列的所有行的实际数据大小都相同。
第二步是"column length view"生成。因为给定列的声明数据长度可能与实际数据长度不同,我们必须创建另一个视图来修复它。
/* Execute:
sqlplus scott/tiger@orcl1 @gen_col_len_view_ddl.sql
*/
SET SQLBLANKLINES ON HEAD OFF
SET PAGESIZE 0 FEEDBACK OFF
SET TRIMSPOOL ON LINE 32000
SET WRAP ON
SET NEWPAGE NONE
SET PAGESIZE 0
SET SPACE 0 LONG 1000
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
set termout off
spool GENERATE_EXTERNAL_TABLE/GENERATED/v_col_len.sql
PROMPT set sqlblanklines on
select '
create or replace view v_col_len as
select rn1,col_name,col_len
from( select rownum rn1,value col_name
from (select * from v_column_list
unpivot ( value
for value_type in (' str
from dual
union all
select '"'||column_name||'"'||(case cid when max_cid then '' else ',' end) len
from
(select column_id cid, max(column_id) over() max_cid,column_name
from all_tab_columns
where table_name=:source_view
order by column_id)
union all
select ')
))),
(select rownum rn2, value col_len
from ( select * from v_column_list
unpivot ( value for value_type in ('
from dual
union all
select '"'||column_name||'_len"'||(case cid when max_cid then '' else ',' end) len
from
(select column_id cid, max(column_id) over() max_cid,column_name
from all_tab_columns
where table_name=:source_view
order by column_id)
union all
select ')
)))
where rn1=rn2 ' from dual;
PROMPT /
--PROMPT exit;;
spool off
既然我们知道一列的结束位置和另一列的开始位置,我们就可以生成外部 table DDL
/* Execute:
sqlplus scott/tiger@orcl1 @gen_external_table_ddl.sql
*/
COLUMN str format a900
set termout on
PROMPT Generating ext table DDL
set termout off
SET SQLBLANKLINES ON
SET PAGESIZE 0
SET TRIMSPOOL ON LINE 32000
SET WRAP ON
SET NEWPAGE NONE
SET PAGESIZE 0
SET SPACE 0
SET LINESIZE 16000
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
set timing off
set time off
set headsep off
set termout off
spool GENERATE_EXTERNAL_TABLE/GENERATED/fixed_width_external_table.sql
PROMPT set sqlblanklines on
PROMPT set termout on
PROMPT PROMPT Creating external table
WITH c as (select * from V_COL_LEN)
select 'DROP TABLE '||:ext_table_name||';' str from dual
union all
select '
set termout off
CREATE TABLE '||:ext_table_name||'
( '
str
from dual union all
select col_name||' CHAR('||col_len||')'||(case rn1 when max_rn then '' else ',' end) str from
(
select col_name,col_len, rn1, max(rn1) over() max_rn
from c)
union all
select ' )
ORGANIZATION external
(
TYPE oracle_loader
DEFAULT DIRECTORY TEST_DIR
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE CHARACTERSET WE8ISO8859P1
READSIZE 1048576
FIELDS LDRTRIM
MISSING FIELD VALUES ARE NULL
REJECT ROWS WITH ALL NULL FIELDS
('
from dual
union all
select col_name||' ('||(CUMTOT-col_len+1)||':'||CUMTOT||') CHAR('||col_len||')'||(case rn1 when max_rn then '' else ',' end) str from (
select col_name,col_len,
SUM(col_len) OVER (ORDER BY rn1) CUMTOT, rn1, max(rn1) over() max_rn
from c)
union all
select ')
)
location
(
'''||:dump_file_name||'''
)
)
'
from dual
/
PROMPT /
--PROMPT exit;;
spool off
为了总结,您可以创建执行所有 3 个步骤的独立脚本。
var dump_file_name varchar2(128);
BEGIN
select 'c_'||export_date||'.dmp' into :dump_file_name
from (select
to_char(sd,'MM')||to_char(sd,'YY') export_date
from (SELECT systimestamp sd FROM DUAL));
END;
/
print :dump_file_name
var ext_table_name varchar2(32);
BEGIN
select 'fixed_width_external_table' into :ext_table_name from dual;
END;
/
print :ext_table_name
var source_view varchar2(32);
BEGIN
select 'V_TEST' into :source_view from dual;
END;
/
print :source_view
PROMPT Generating column list view (V_COLUMN_LIST) based on source view
@GENERATE_EXTERNAL_TABLE/gen_column_list_view_ddl.sql
@GENERATE_EXTERNAL_TABLE/GENERATED/v_column_list.sql
PROMPT Generating column lengths view (V_COL_LEN) based on source view
@GENERATE_EXTERNAL_TABLE/gen_col_len_view_ddl.sql
@GENERATE_EXTERNAL_TABLE/GENERATED/v_col_len.sql
PROMPT Generating external table DDL based on V_COL_LEN
@GENERATE_EXTERNAL_TABLE/gen_external_table_ddl.sql
@GENERATE_EXTERNAL_TABLE/GENERATED/fixed_width_external_table.sql
SET PAGESIZE 99
SET ECHO on
SET FEEDBACK on
SET VERIFY OFF
SET HEADING on
set timing on
set time on
set headsep on
set termout on
var exported_cnt varchar2(32);
BEGIN
EXECUTE IMMEDIATE 'select /*+PARALLEL (t,auto)*/ count(*) rows_exported from '||:ext_table_name||' t' INTO :exported_cnt;
END;
/
print :exported_cnt
此脚本将从 Oracle 中的现有视图或堆 table 名称创建外部 table DDL。
我最初写 if 是为了在 Oracle 中进行常规到外部 table 的转换。
我编写了一个程序来将视图内容提取到 Oracle 中的文件系统中。 现在客户想要测试文件的内容。 我如何在 Oracle 中以通用方式执行此操作?
程序:
CREATE OR REPLACE PROCEDURE PRC_EXTRACT_VIEW (p_view_name VARCHAR2, p_timestamp VARCHAR2)
AUTHID CURRENT_USER
IS
/* Run:
set serveroutput on timing on
begin PRC_EXTRACT_VIEW('V_TEST'); end;
*/
p_query VARCHAR2(512) :='SELECT * FROM '||p_view_name;
p_separator VARCHAR2(8) := '';
p_dir_obj VARCHAR2(126) :='TEST_DIR';
p_filename VARCHAR2(128) :=p_view_name||'_'||p_timestamp||'.csv';
l_output utl_file.file_type;
l_cnt NUMBER := 0;
l_theCursor INTEGER DEFAULT dbms_sql.open_cursor;
l_columnValue VARCHAR2(2000):='';
l_status INTEGER;
l_separator VARCHAR2(10) DEFAULT '';
c NUMBER;
d NUMBER;
col_cnt INTEGER;
f BOOLEAN;
rec_tab DBMS_SQL.DESC_TAB;
col_num NUMBER;
v_sql dbms_sql.varchar2a;
v_sql_1 varchar2(32767);
pn varchar2(32):='PRC_EXTRACT_VIEW';
v_ErrMsg varchar2(2000);
v_Errcode varchar2(60);
v_etl_btch_id number;
v_filename varchar2(256):=p_filename;
BEGIN
l_output := utl_file.fopen( p_dir_obj, v_filename, 'w',32767 );
DBMS_SQL.PARSE(l_theCursor, p_query, DBMS_SQL.NATIVE);
FOR i IN 1 .. 255
LOOP
BEGIN
dbms_sql.define_column( l_theCursor, i, l_columnValue, 2000 );
EXCEPTION
WHEN OTHERS THEN
IF ( SQLCODE = -1007 ) THEN
EXIT;
ELSE
raise;
END IF;
END;
END LOOP;
dbms_sql.define_column( l_theCursor, 1, l_columnValue, 2000 );
d := DBMS_SQL.EXECUTE(l_theCursor);
DBMS_SQL.DESCRIBE_COLUMNS(l_theCursor, col_cnt, rec_tab);
LOOP
EXIT
WHEN ( dbms_sql.fetch_rows(l_theCursor) <= 0 );
l_separator := '';
FOR i IN 1 .. col_cnt
LOOP
dbms_sql.column_value( l_theCursor, i, l_columnValue );
--dbms_output.put_line(l_columnValue);
utl_file.put( l_output, l_separator || l_columnValue );
l_separator := p_separator;
END LOOP;
utl_file.new_line( l_output );
l_cnt := l_cnt+1;
END LOOP;
dbms_sql.close_cursor(l_theCursor);
utl_file.fclose( l_output );
/*--TODO
EXCEPTION
WHEN OTHERS THEN
*/
END PRC_EXTRACT_VIEW;
/
客户对文件中的内容有些醉意。 有没有办法在同一个会话或新会话中读取转储文件的内容? (用于测试目的)
您可以根据您用于假脱机的视图生成外部 table。
对于相同的 p_view_name
你做一次然后即使 p_timestamp
改变新的摘录你也可以改变现有的永恒 table:
alter table ext_table location ('new_filename');
如果p_view_name
改变了那么你必须重新运行table生成脚本。
我提供的解决方案只有在数据文件中的每一列的所有行都具有固定长度时才有效。
首先,您必须生成一个 "column list view" - 包含所有声明的列名称和源视图长度的扁平化结构。
/* Execute:
sqlplus scott/tiger@orcl1 @gen_column_list_view_ddl.sql
*/
SET SQLBLANKLINES ON
SET PAGESIZE 0 FEEDBACK OFF
SET TRIMSPOOL ON LINE 32000
SET WRAP ON
SET NEWPAGE NONE
SET PAGESIZE 0
SET SPACE 0 LONG 1000
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
set termout off
spool GENERATE_EXTERNAL_TABLE/GENERATED/v_column_list.sql
PROMPT set sqlblanklines on
select '
create or replace view v_column_list
as select ' from dual
union all
select ''''||column_name||''' '||column_name||', length('||column_name||') "'||column_name||'_len"'||(case cid when max_cid then '' else ',' end) len
from
(select column_id cid, max(column_id) over() max_cid,column_name
from all_tab_columns
where table_name=:source_view order by column_id)
union all
select '
from '||:source_view||'
where rownum <2' from dual;
PROMPT /
--PROMPT exit;;
spool off
因为您在没有分隔符的情况下提取,这意味着它的长度是固定的,并且给定列的所有行的实际数据大小都相同。
第二步是"column length view"生成。因为给定列的声明数据长度可能与实际数据长度不同,我们必须创建另一个视图来修复它。
/* Execute:
sqlplus scott/tiger@orcl1 @gen_col_len_view_ddl.sql
*/
SET SQLBLANKLINES ON HEAD OFF
SET PAGESIZE 0 FEEDBACK OFF
SET TRIMSPOOL ON LINE 32000
SET WRAP ON
SET NEWPAGE NONE
SET PAGESIZE 0
SET SPACE 0 LONG 1000
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
set termout off
spool GENERATE_EXTERNAL_TABLE/GENERATED/v_col_len.sql
PROMPT set sqlblanklines on
select '
create or replace view v_col_len as
select rn1,col_name,col_len
from( select rownum rn1,value col_name
from (select * from v_column_list
unpivot ( value
for value_type in (' str
from dual
union all
select '"'||column_name||'"'||(case cid when max_cid then '' else ',' end) len
from
(select column_id cid, max(column_id) over() max_cid,column_name
from all_tab_columns
where table_name=:source_view
order by column_id)
union all
select ')
))),
(select rownum rn2, value col_len
from ( select * from v_column_list
unpivot ( value for value_type in ('
from dual
union all
select '"'||column_name||'_len"'||(case cid when max_cid then '' else ',' end) len
from
(select column_id cid, max(column_id) over() max_cid,column_name
from all_tab_columns
where table_name=:source_view
order by column_id)
union all
select ')
)))
where rn1=rn2 ' from dual;
PROMPT /
--PROMPT exit;;
spool off
既然我们知道一列的结束位置和另一列的开始位置,我们就可以生成外部 table DDL
/* Execute:
sqlplus scott/tiger@orcl1 @gen_external_table_ddl.sql
*/
COLUMN str format a900
set termout on
PROMPT Generating ext table DDL
set termout off
SET SQLBLANKLINES ON
SET PAGESIZE 0
SET TRIMSPOOL ON LINE 32000
SET WRAP ON
SET NEWPAGE NONE
SET PAGESIZE 0
SET SPACE 0
SET LINESIZE 16000
SET ECHO OFF
SET FEEDBACK OFF
SET VERIFY OFF
SET HEADING OFF
set timing off
set time off
set headsep off
set termout off
spool GENERATE_EXTERNAL_TABLE/GENERATED/fixed_width_external_table.sql
PROMPT set sqlblanklines on
PROMPT set termout on
PROMPT PROMPT Creating external table
WITH c as (select * from V_COL_LEN)
select 'DROP TABLE '||:ext_table_name||';' str from dual
union all
select '
set termout off
CREATE TABLE '||:ext_table_name||'
( '
str
from dual union all
select col_name||' CHAR('||col_len||')'||(case rn1 when max_rn then '' else ',' end) str from
(
select col_name,col_len, rn1, max(rn1) over() max_rn
from c)
union all
select ' )
ORGANIZATION external
(
TYPE oracle_loader
DEFAULT DIRECTORY TEST_DIR
ACCESS PARAMETERS
(
RECORDS DELIMITED BY NEWLINE CHARACTERSET WE8ISO8859P1
READSIZE 1048576
FIELDS LDRTRIM
MISSING FIELD VALUES ARE NULL
REJECT ROWS WITH ALL NULL FIELDS
('
from dual
union all
select col_name||' ('||(CUMTOT-col_len+1)||':'||CUMTOT||') CHAR('||col_len||')'||(case rn1 when max_rn then '' else ',' end) str from (
select col_name,col_len,
SUM(col_len) OVER (ORDER BY rn1) CUMTOT, rn1, max(rn1) over() max_rn
from c)
union all
select ')
)
location
(
'''||:dump_file_name||'''
)
)
'
from dual
/
PROMPT /
--PROMPT exit;;
spool off
为了总结,您可以创建执行所有 3 个步骤的独立脚本。
var dump_file_name varchar2(128);
BEGIN
select 'c_'||export_date||'.dmp' into :dump_file_name
from (select
to_char(sd,'MM')||to_char(sd,'YY') export_date
from (SELECT systimestamp sd FROM DUAL));
END;
/
print :dump_file_name
var ext_table_name varchar2(32);
BEGIN
select 'fixed_width_external_table' into :ext_table_name from dual;
END;
/
print :ext_table_name
var source_view varchar2(32);
BEGIN
select 'V_TEST' into :source_view from dual;
END;
/
print :source_view
PROMPT Generating column list view (V_COLUMN_LIST) based on source view
@GENERATE_EXTERNAL_TABLE/gen_column_list_view_ddl.sql
@GENERATE_EXTERNAL_TABLE/GENERATED/v_column_list.sql
PROMPT Generating column lengths view (V_COL_LEN) based on source view
@GENERATE_EXTERNAL_TABLE/gen_col_len_view_ddl.sql
@GENERATE_EXTERNAL_TABLE/GENERATED/v_col_len.sql
PROMPT Generating external table DDL based on V_COL_LEN
@GENERATE_EXTERNAL_TABLE/gen_external_table_ddl.sql
@GENERATE_EXTERNAL_TABLE/GENERATED/fixed_width_external_table.sql
SET PAGESIZE 99
SET ECHO on
SET FEEDBACK on
SET VERIFY OFF
SET HEADING on
set timing on
set time on
set headsep on
set termout on
var exported_cnt varchar2(32);
BEGIN
EXECUTE IMMEDIATE 'select /*+PARALLEL (t,auto)*/ count(*) rows_exported from '||:ext_table_name||' t' INTO :exported_cnt;
END;
/
print :exported_cnt
此脚本将从 Oracle 中的现有视图或堆 table 名称创建外部 table DDL。
我最初写 if 是为了在 Oracle 中进行常规到外部 table 的转换。