The fields are in different physical records, because of the newlines. With SQL*Loader you can concatenate
or continueif
to assemble logical records from physical records, but I don't think the external table driver supports that. Or loading XMLType directly.
You could load it via a CLOB field. Create a data file in the same DIR
directory which has the real XML file name in it: say xmlfiles.txt
which just contains the value xmlFILE.xml
(but could have multiple rows, each referring to a different XML file). Then your external table would be:
create table test (xml_clob clob)
organization external
(
type oracle_loader
default directory dir
access parameters
(
records delimited by newline
nobadfile nodiscardfile nologfile
fields missing field values are null
reject rows with all null fields
(
xml_filename char(80)
)
column transforms (xml_clob from lobfile (xml_filename) from (dir) clob)
)
location ('xmlfiles.txt')
)
reject limit unlimited;
Notice that the location
is now the new file containing the real file's name; and the column transforms
clause is using the real file name - from the xml_filename
extracted from the new file - to actually load the CLOB data. You can't have a CLOB directly in the loader data file, it has to be in a separate file that is referenced.
The test
table now has that XML file stored as a CLOB, and you can convert it to an XMLType to manipulate it, e.g. as part of an XMLTable clause:
select x.*
from test t
cross join xmltable('/REC/static_data/summary/names/name'
passing xmltype(t.xml_clob)
columns d_name varchar2(20) path 'd_name',
f_name varchar2(20) path 'f_name',
w varchar2(20) path 'w'
) x;
D_NAME F_NAME W
-------------------- -------------------- --------------------
John John, RICHARD jOHN, MI
Peter Peter, RICHARD Pet, MI
The xmltype(t.xml_clob)
is doing the conversion, so the XPath is applied to an XML document formed from the CLOB value in the table.
There is a section of the documentation that addresses loading XML content. If you get it into the DB as XML then you can manipulate it to create relational data.
Using the mechanism you tried in your previous question, and repeating some parts of that answer, you can create the staging table with an XMLType column and load from a bfile:
create table test (xml_file xmltype)
xmltype xml_file store as securefile binary xml;
insert into test (xml_file)
select xmltype(bfilename('DIR', 'xmlFILE.xml'), nls_charset_id('WE8ISO8859P1'))
from dual;
You can then use an XMLTable clause again to get the data in a relational form:
select x.*
from test t
cross join xmltable('/REC/static_data/summary/names/name'
passing t.xml_file
columns seq_no number path '@seq_no',
d_name varchar2(20) path 'd_name',
f_name varchar2(20) path 'f_name',
w varchar2(20) path 'w'
) x;
SEQ_NO D_NAME F_NAME W
---------- -------------------- -------------------- --------------------
1 John John, RICHARD jOHN, MI
2 Peter Peter, RICHARD Pet, MI
I've included the seq_no
just to show how to get an attribute value.
The XPath has to match your real file; I added the closing tags for summary, static_data and REC to make it valid, but if you also have an enclosing records
level (as shown in the previous question) then you'd need to include that in the XPath, and also specify the namespace:
...
cross join xmltable(xmlnamespaces(default 'http://xxxxxxxxxxxxxx'),
'/records/REC/static_data/summary/names/name'
passing t.xml_file
...
If you want to get data from two levels you can pull it out as shown in your comment, but not if any of the child nodes are repeated; here you have more then one name for each REC
, so you'd get ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence
. You could use FLWOR syntax go create new elements combining the data, but it might be simpler to extract the names
node and pass that to a second XMLTable:
select x.cd_uid, y.*
from test t
cross join xmltable(xmlnamespaces(default 'http://xxxxxxxxxxxxxx'),
'/records/REC'
passing t.xml_file
columns cd_uid varchar2(10) path 'UID',
names xmltype path 'static_data/summary/names'
) x
cross join xmltable(xmlnamespaces(default 'http://xxxxxxxxxxxxxx'),
'names/name'
passing x.names
columns seq_no number path '@seq_no',
d_name varchar2(20) path 'd_name',
f_name varchar2(20) path 'f_name',
w varchar2(20) path 'w'
) y;
CD_UID SEQ_NO D_NAME F_NAME W
---------- ---------- -------------------- -------------------- --------------------
yyyyyyyyy 1 John John, RICHARD jOHN, MI
yyyyyyyyy 2 Peter Peter, RICHARD Pet, MI
The names
sub-XML is extracted as x.names
, and the individual elements are then pulled out of that in the y
XMLTable. You can do that for multiple levels if necessary, and pull a mix of singleton and sequence data out of each, depending how complicated your XML structure is.