import xml to oracle database table - sql

I have this sample xml my requirement is to parse it and fetch values from it's various nodes and insert them into one of oracle table.
<?xml version="1.0" encoding="UTF-8"?>
<Reporting>
<Selection>69</Selection>
<MonthEndDate>9/30/2016</MonthEndDate>
<Email>
<Name>abc</Name>
<Address>abc#gmail.com</Address>
</Email>
<Request>
<Port_id_list>
<Port_id>1901</Port_id>
<Port_id>1902</Port_id>
<Port_id>1903</Port_id>
</Port_id_list>
</Request>
</Reporting>
How can we do this?
Please help.

EDITED
For example:
WITH my_data AS
(SELECT xmltype('<?xml version="1.0" encoding="UTF-8"?>
<Reporting>
<Selection>69</Selection>
<MonthEndDate>9/30/2016</MonthEndDate>
<Email>
<Name>abc</Name>
<Address>abc#gmail.com</Address>
</Email>
<Request>
<Port_id_list>
<Port_id>1901</Port_id>
<Port_id>1902</Port_id>
<Port_id>1903</Port_id>
</Port_id_list>
</Request>
</Reporting>') my_xml
FROM dual)
SELECT extractValue(md.my_xml, 'Reporting/Selection/text()') selection_id,
extractValue(value(port_ids), 'Port_id/text()') port_id
FROM my_data md,
TABLE(XMLSequence (md.my_xml.extract ('Reporting/Request/Port_id_list/Port_id'))) port_ids;
If your XML has node with children create one-to-many with TABLE and XMLSequence.

You should preferably use XMLTABLE as the extractValue was deprecated
Here an example selecting teh ports with the (denormalized) parten attributes.
I added also posr sequence to preserve order of the ports.
WITH t AS
(SELECT xmltype('<?xml version="1.0" encoding="UTF-8"?>
<Reporting>
<Selection>69</Selection>
<MonthEndDate>9/30/2016</MonthEndDate>
<Email>
<Name>abc</Name>
<Address>abc#gmail.com</Address>
</Email>
<Request>
<Port_id_list>
<Port_id>1901</Port_id>
<Port_id>1902</Port_id>
<Port_id>1903</Port_id>
</Port_id_list>
</Request>
</Reporting>') xml
FROM dual)
select
x.Selection,x.MonthEndDate,x.emailName, x.emailAddress,
o.port_seq, o.port_id
from t,
XMLTable(
'for $i in /Reporting
return $i'
passing t.xml
columns
Selection varchar2(30) path 'Selection',
MonthEndDate varchar2(30) path 'MonthEndDate',
emailName varchar2(30) path 'Email/Name',
emailAddress varchar2(30) path 'Email/Address',
Port_id_list XMLType path '//Port_id'
) x,
XMLTable(
'./Port_id'
passing (x.Port_id_list)
columns
port_seq for ordinality,
port_id varchar2(30) path '/Port_id'
) o
;
SELECTION MONTHENDDATE EMAILNAME EMAILADDRESS PORT_SEQ PORT_ID
------------------------------ ------------------------------ ------------------------------ ------------------------------ ---------- ------------------------------
69 9/30/2016 abc abc#gmail.com 1 1901
69 9/30/2016 abc abc#gmail.com 2 1902
69 9/30/2016 abc abc#gmail.com 3 1903

Related

Oracle - extracting XML version and encoding from XML prolog in XMLType

Let's say we have following XMLType in Oracle:
<?xml version="1.0" encoding="UTF-8"?>
<root>
</root>
I need to get XML version and encoding values or whole XML prolog preferably with XML functions, f. ex. XMLTABLE. Is that possible? Or only with string/CLOB functions?
We use Oracle 19c.
Is that possible?
No, unfortunately, it isn't, because XML functions like XMLTable work with XMLType which parses your input xml (from your NLS_LANG charset if it is CLOB or Varchar2, and from specified charset if it is BLOB) and store it in own internal codepage and when you get it, oracle returns it in your NLS_LANG:
SQL> with v as (select q'[<?xml version="1.1" encoding="blabla"?><root/>]' as vXML from dual)
2 select xmltype(vXML) x from v;
X
------------------------------------------------
<?xml version="1.1" encoding="US-ASCII"?>
<root/>
And look what if I change my NLS_LANG to UTF8:
SQL> with v as (select q'[<?xml version="1.1" encoding="blabla"?><root/>]' as vXML from dual)
2 select xmltype(vXML) x from v;
X
-----------------------------------------
<?xml version="1.1" encoding="UTF-8"?>
<root/>
SQL> ! env | grep NLS_LANG
NLS_LANG=american_america.UTF8

Extracting multiple values from BLOB as XML

I have an XML like this in a BLOB column:
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
<Event>
<year>1983</year>
<Country>
<Location>Lisbon</Location>
<Type>Political</Type>
</Country>
</Event>
<Event>
<Country>
<Location>USA</Location>
<Type>Entertainment</Type>
<year>2016</year>
</Country>
</Event>
</history>
</document>
As you can see the year can be either in the event level or at country level. There can be multiple events and multiple countries per event. This whole XML is stored in a BLOB column in Oracle. I need to extract the value of the year or better check if the year is 2000 and if so return the primary key of the row.
I used EXISTSNODE to check if the year tag is present.
select pk from table where XMLType(blobdata, nls_charset_id('UTF8')).EXISTSNODE('/Document/history/Event[*]/year',
'xmlns="urn:xyzns"') = 1 and EXTRACTVALUE(XMLTYPE(blobdata, nls_charset_id('UTF8')), '/Document/history/Event[*]/year/text()',
'xmlns="urn:xyzns"') = '2000';
However this fails and the extractvalue query returns multiple nodes, so I changed the parameter to '/Document/history/Event[1]/year/text()' to check and it works. However this wouldnt be enough as it only checks the first event tag.
I looked at other questions here and one of the options was to use XMLTABLE since extractvalue is deprecated. I am having trouble understanding the parameters given inside the XMLTABLE. Could someone explain how to use XMLTABLE in this scenario? I should point out that the original datatype is BLOB and not CLOB. Thank you.
Use XMLTABLE to get values for both locations and then use COALESCE to show whichever is not NULL:
SELECT COALESCE( year, country_year ) AS year,
location,
type
FROM table_name t
CROSS APPLY XMLTABLE(
XMLNAMESPACES( DEFAULT 'urn:xyzns' ),
'/document/history/Event'
PASSING XMLTYPE(t.blobdata, nls_charset_id('UTF8'))
COLUMNS
year NUMBER(4,0) PATH './year',
country_year NUMBER(4,0) PATH './Country/year',
location VARCHAR2(200) PATH './Country/Location',
type VARCHAR2(200) PATH './Country/Type'
) x
Which, for the sample data:
CREATE TABLE table_name ( blobdata BLOB );
INSERT INTO table_name
VALUES (
UTL_RAW.CAST_TO_RAW(
'<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="urn:xyzns">
<history>
<Event>
<year>1983</year>
<Country>
<Location>Lisbon</Location>
<Type>Political</Type>
</Country>
</Event>
<Event>
<Country>
<Location>USA</Location>
<Type>Entertainment</Type>
<year>2016</year>
</Country>
</Event>
</history>
</document>'
)
);
Outputs:
YEAR | LOCATION | TYPE
---: | :------- | :------------
1983 | Lisbon | Political
2016 | USA | Entertainment
db<>fiddle here

SQL Oracle exctrac value from XML unordered Collection

I have a clob with XML like this:
<Data>
<Email_List>
<Email>
<EmailPrimaryFlag>Y</EmailPrimaryFlag>
<EmailAddress>test#test.it</EmailAddress>
</Email>
<Email>
<EmailPrimaryFlag>N</EmailPrimaryFlag>
<EmailAddress>test2#test2.it</EmailAddress>
</Email>
<Email>
<EmailPrimaryFlag>N</EmailPrimaryFlag>
<EmailAddress>test3#test3.it</EmailAddress>
</Email>
</Email_List>
<User>Name_User</User>
<City>City_test</City>
</Data>
I need to do an extract only for PrimaryFlag=Y
Usually i do something like this:
select
extract(xmltype(PAYLOAD),'//*:User/text()').getStringVal() as User,
extract(xmltype(PAYLOAD),'//*:City/text()').getStringVal() as City,
but i don't know how i can extract for mail with PrimaryFlag=Y
Special Thanks
Michel
Avoid using EXRACT() as it is deprecated.
with t (payload) as (
select to_clob('<Data>
<Email_List>
<Email>
<EmailPrimaryFlag>Y</EmailPrimaryFlag>
<EmailAddress>test#test.it</EmailAddress>
</Email>
<Email>
<EmailPrimaryFlag>N</EmailPrimaryFlag>
<EmailAddress>test2#test2.it</EmailAddress>
</Email>
<Email>
<EmailPrimaryFlag>N</EmailPrimaryFlag>
<EmailAddress>test3#test3.it</EmailAddress>
</Email>
</Email_List>
<User>Name_User</User>
<City>City_test</City>
</Data>') from dual)
select x.* from t, xmltable(
'/Data'
passing xmltype(payload)
columns
primary_email varchar2(60) path 'Email_List/Email/EmailAddress[../EmailPrimaryFlag = "Y"]',
username varchar2(60) path 'User',
city varchar2(60) path 'City'
)x;
PRIMARY_EMAIL USERNAME CITY
------------------------------------------------------------ ------------------------------------------------------------ ------------------------------------------------------------
test#test.it Name_User City_test

Parsing XML with XMLTable n Oracle

I have one xml which holds customer and order details. I am parsing it using XMLTable. I think I am giving the correct XPath but I am getting no output.
Here is what I have tried. Hoping for assistance.
**DECLARE
l_raw_xml CLOB:= '<?xml version="1.0" encoding="utf-8"?>
<Root
xmlns="http://www.adventure-works.com">
<Customers>
<Customer CustomerID="GREAL">
<CompanyName>Great Lakes Food Market</CompanyName>
<ContactName>Howard Snyder</ContactName>
<ContactTitle>Marketing Manager</ContactTitle>
<Phone>(503) 555-7555</Phone>
<FullAddress>
<Address>2732 Baker Blvd.</Address>
<City>Eugene</City>
<Region>OR</Region>
<PostalCode>97403</PostalCode>
<Country>USA</Country>
</FullAddress>
</Customer>
</Customers>
<Orders>
<Order>
<CustomerID>LETSS</CustomerID>
<EmployeeID>6</EmployeeID>
<OrderDate>1997-11-10T00:00:00</OrderDate>
<RequiredDate>1997-12-08T00:00:00</RequiredDate>
<ShipInfo ShippedDate="1997-11-21T00:00:00">
<ShipVia>2</ShipVia>
<Freight>45.97</Freight>
<ShipName>Let Stop N Shop</ShipName>
<ShipAddress>87 Polk St. Suite 5</ShipAddress>
<ShipCity>San Francisco</ShipCity>
<ShipRegion>CA</ShipRegion>
<ShipPostalCode>94117</ShipPostalCode>
<ShipCountry>USA</ShipCountry>
</ShipInfo>
</Order>
</Orders>
</Root>';
l_xml_type XMLTYPE:=XMLTYPE.createXML(l_raw_xml);
cursor c_make_xml_object
is
SELECT x.*,
y.*
FROM XMLTABLE('/Root/Customers/Customer'
PASSING l_xml_type
COLUMNS
CustomerID VARCHAR2(255) PATH '#CustomerID',
CompanyName VARCHAR2(255) PATH 'CompanyName',
FullAddress XMLTYPE PATH 'FullAddress') x,
XMLTABLE('/FullAddress'
PASSING x.FullAddress
COLUMNS
Address VARCHAR2(255) PATH 'Address',
City VARCHAR2(255) PATH 'City') y ;
BEGIN
FOR rec IN c_make_xml_object
loop
DBMS_OUTPUT.PUT_LINE(rec.CustomerID);
DBMS_OUTPUT.PUT_LINE(rec.CompanyName);
END LOOP;
END;
/**
Do I have to account for the namespace? I had added namespace code but it did not work/
Change to this to include a default namespace:
FROM XMLTABLE(xmlnamespaces(default 'http://www.adventure-works.com'),
'/Root/Customers/Customer' ....
....
XMLTABLE(xmlnamespaces(default 'http://www.adventure-works.com'), '/FullAddress'
.....
Yes, a proper namespace definition is needed within the SQL query such as below :
SELECT x.*, y.*
FROM XML_TAB t,
XMLTABLE(XMLnamespaces('http://www.adventure-works.com' as "ns"),
'ns:Root/ns:Customers/ns:Customer'
PASSING t.xml_data
COLUMNS
CustomerID VARCHAR2(255) PATH '#CustomerID',
CompanyName VARCHAR2(255) PATH 'ns:CompanyName',
FullAddress XMLTYPE PATH 'ns:FullAddress'
) x,
XMLTABLE(XMLnamespaces('http://www.adventure-works.com' as "ns"),
'ns:FullAddress'
PASSING x.FullAddress
COLUMNS
Address VARCHAR2(255) PATH 'ns:Address',
City VARCHAR2(255) PATH 'ns:City') y ;
Demo with SQL
Demo with PL/SQL

Extracting a node where xmlns is set to blank

I'm having difficulties extracting the value from certain nodes in an XML structure using XMLTABLE. Below query works perfectly when you remove the xmlns="" attribute from the SubListItem nodes. And as you can see, the XML already has a default namespace. I honestly have no clue how I can deal with this "blanking out" of the namespace on certain nodes like this.
For further clarification, the creation of this XML is not within my control and is provided by a third-party. I've also changed the names of the nodes and the content from the delivered files while preserving the structure of the XML.
SELECT f.airline, f.flightnumber, fl.gate
FROM xmltable(
xmlnamespaces(
default 'http://some/name.space',
'http://www.w3.org/2001/XMLSchema' as "xsd",
'http://www.w3.org/2001/XMLSchema-instance' as "xsi"
),
'Body/Flight'
passing xmltype(
'<?xml version="1.0" encoding="utf-16"?>
<Body xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://some/name.space">
<Sender>
<System>ConnectionManagement</System>
</Sender>
<Flight>
<Airline>ABC</Airline>
<Number>1234</Number>
<SubList>
<SubListItem xmlns="">
<Gate>X</Gate>
</SubListItem>
<SubListItem xmlns="">
<Gate>Y</Gate>
</SubListItem>
<SubListItem xmlns="">
<Gate>Z</Gate>
</SubListItem>
</SubList>
</Flight>
</Body>'
)
columns airline varchar2(100) path 'Airline'
, flightNumber VARCHAR2(5) path 'Number'
, subList XMLTYPE path 'SubList'
) f
, xmltable (
xmlnamespaces( default 'http://some/name.space'),
'/SubList/SubListItem'
passing f.subList
columns gate varchar2(5) path 'Gate'
) fl
;
How can I target the Gate node when the XML looks like this?
Leave the default namespace alone in the second XMLTable, and specify a named namespace for the path you do have:
...
, xmltable (
xmlnamespaces( 'http://some/name.space' as "ns"),
'/ns:SubList/SubListItem'
passing f.subList
columns gate varchar2(5) path 'Gate'
) fl
;
AIRLINE FLIGH GATE
---------- ----- -----
ABC 1234 X
ABC 1234 Y
ABC 1234 Z
The SubList still has to match that, but as the child nodes don't the default is incorrect the way you have it. If you remove the xmlns="" as you mentioned in the question then that inherits the namespace from its parent, so your default works. With that override to no-namespace you can't use a default.