how to use xmltable to read values from a clob? - oracle

I'm trying to get the values of the attributes from table MVR_DTL in column VENDOR_XML. VENDOR_XML is of datatype clob and contains an xml that looks like this
<?xml version="1.0" encoding="UTF-8"?>
<MVRCHPINFF_1.0>
<MVRRecLoop>
<CLoop>
<CRec>
<C_MVRNumberAddr>ROMAN GENERAL</C_MVRNumberAddr>
</CRec>
<CRec>
<C_MVRNumberAddr>ROMAN ST</C_MVRNumberAddr>
</CRec>
<CRec>
<C_MVRNumberAddr>ROMAN CITY, ROME 111111</C_MVRNumberAddr>
</CRec>
</CLoop>
</MVRRecLoop>
</MVRCHPINFF_1.0>
I tried running
SELECT c.Address
from MVR_DTL M, XMLTABLE('/MVRCHPINFF_1.0/MVRRecLoop/CLoop/CRec'
passing XMLTYPE(M.VENDOR_XML)
columns Address XMLTYPE PATH './C_MVRNumberAddr') c;
I'm expecting something like
ROMAN GENERAL ROMAN ST ROMAN CITY, ROME 111111
but i only get 'Statement has failed, however your database does not return any error information.'
Oracle version 12.2.0.1.0

SELECT c.Address from MVR_DTL M,
XMLTABLE(
'string-join(/MVRCHPINFF_1.0/MVRRecLoop/CLoop/CRec/C_MVRNumberAddr, " ")'
passing XMLTYPE(M.VENDOR_XML)
columns Address varchar2(200) PATH '.') c;

Related

updating CLOB column with multiple namespace in Oracle

I am trying to update an element which is inside a clob column in oracle DB.
First challenge I am facing is that my clob xml has 2 namespaces and I am not able to get that working .
<?xml version="1.0"?>
<esbmsg:EsbMessage xmlns:esbmsg="http://www.test.com/esb/message/1.0">
<esbmsg:Body>
<Transaction xmlns="http://test.com">
<test-element>
<finalElement>false</finalElement>
</test-elemen>
</Transaction>
</esbmsg:Body>
</esbmsg:EsbMessage>
select x.* from cc_messagehistory y
cross join xmltable(
xmlnamespaces('http://www.test.com/esb/message/1.0' as "esbmsg",
'http://test.com ' ),
'/esbmsg:EsbMessage'
passing xmltype.createxml(y.payload)
factext varchar2(10) path '/esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement'
) x;
ORA-19102: XQuery string literal expected
19102. 00000 - "XQuery string literal expected"
*Cause: The string literal containing the XQuery expression was missing.
*Action: Specify the XQuery expression as a string literal. Error at Line: 64 Column: 99
The immediate cause of the ORA-01902 is that you missed the default keyword:
xmlnamespaces('http://www.test.com/esb/message/1.0' as "esbmsg",
default 'http://test.com'),
I've removed the extra space at the end of the URI, which would cause problems later. But you are also missing the columns keyword, and you can simplify the conversion of the CLOB value to an XMLType.
Putting that together, and with a CTE to supply your (corrected) sample XML:
-- CTE for sample data
with cc_messagehistory(payload) as (
select to_clob('<?xml version="1.0"?>
<esbmsg:EsbMessage xmlns:esbmsg="http://www.test.com/esb/message/1.0">
<esbmsg:Body>
<Transaction xmlns="http://test.com">
<test-element>
<finalElement>false</finalElement>
</test-element>
</Transaction>
</esbmsg:Body>
</esbmsg:EsbMessage>') from dual
)
-- actual query
select x.*
from cc_messagehistory y
cross join xmltable (
xmlnamespaces (
'http://www.test.com/esb/message/1.0' as "esbmsg",
default 'http://test.com'
),
'/esbmsg:EsbMessage'
passing xmltype(y.payload)
columns factext varchar2(10)
path '/esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement'
) x;
FACTEXT
----------
false
For the update you could do something like:
update cc_messagehistory y
set payload = XMLSerialize(document
XMLQuery('declare default element namespace "http://test.com"; (: :)
declare namespace esbmsg="http://www.test.com/esb/message/1.0"; (: :)
copy $i := $xml modify (
for $j in $i//esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement
return replace value of node $j with $new
)
return $i'
passing xmltype(y.payload) as "xml",
'true' AS "new"
returning content
)
indent size=2
)
where xmlexists('declare default element namespace "http://test.com"; (: :)
declare namespace esbmsg="http://www.test.com/esb/message/1.0"; (: :)
$xml//esbmsg:EsbMessage/esbmsg:Body/Transaction/test-element/finalElement[text()="false"]'
passing xmltype(y.payload) as "xml");
which transforms that source CLOB into:
<?xml version="1.0"?>
<esbmsg:EsbMessage xmlns:esbmsg="http://www.test.com/esb/message/1.0">
<esbmsg:Body>
<Transaction xmlns="http://test.com">
<test-element>
<finalElement>true</finalElement>
</test-element>
</Transaction>
</esbmsg:Body>
</esbmsg:EsbMessage>
db<>fiddle (works on 18c; errors on 11gR2, but patch levels may make a difference; also tested successfully elsewhere on 12cR1)

oracle xml parsing with multi rows

query from xml not return rows.
I running this query but not return rows.
The my xml is :
<?xml version="1.0" encoding="UTF-8"?>
<ns0:testata xmlns:ns0="http://siete">
<ns0:product>
<ns0:YEAR>2019</ns0:YEAR>
<ns0:PERIOD>1</ns0:PERIOD>
</ns0:product>
<ns0:product>
<ns0:YEAR>2019</ns0:YEAR>
<ns0:PERIOD>2</ns0:PERIOD>
</ns0:product>
</ns0:testata>
My query is
FROM XMLTABLE('/testata/product'
PASSING
(select xmltype(t.XML1) doc
from tb_test t)
COLUMNS
name varchar2(4) PATH './YEAR'
) xmlt
0 rows
please help me
Your XML document has a namespace, so you either need to wildcard the nodes in your XMLTable call, or - preferably - supply the same namespace information and prefixes:
-- CTE for sample data
with tb_test (xml1) as (select '<?xml version="1.0" encoding="UTF-8"?>
<ns0:testata xmlns:ns0="http://siete">
<ns0:product>
<ns0:YEAR>2019</ns0:YEAR>
<ns0:PERIOD>1</ns0:PERIOD>
</ns0:product>
<ns0:product>
<ns0:YEAR>2019</ns0:YEAR>
<ns0:PERIOD>2</ns0:PERIOD>
</ns0:product>
</ns0:testata>' from dual
)
-- actual query
select x.year
from tb_test t
cross join xmltable(
xmlnamespaces('http://siete' as "ns0"),
'/ns0:testata/ns0:product'
passing xmltype(t.xml1)
columns year number path 'ns0:YEAR'
) x;
YEAR
----------
2019
2019

Reading xml using Oracle

We have to read a xml using pl/sql. The top few lines of the xml are pasted below. In the xml, for one Node,there is one Equipment. For one Equipment, there are multiple Cabinet. For one Cabinet there are multiple Subrack & for one Subrack there are multiple Boards.
We have developed a below query to parse.
Step-1:
create table emp_xml of xmltype xmltype store as securefile binary xml;
Step-2:
insert into emp_xml values (xmltype(bfilename('XML_DIR','ahm_2015_04_01_172428.xml'), nls_charset_id('AL32UTF8') ));
Step-3:
select * from emp_xml;
Step-4:
select x.*
from emp_xml t,
xmltable(xmlnamespaces(default 'http://www.ericsson.com/axe/export/hw'(http :/ /
www.ericsson.com / axe /
export /
hw%27)),
'/NetworkInventory/Node' passing t.object_value columns
SiteName varchar2(10) path '#Name',
SiteType varchar2(10) path '#Type',
BuildingPractice varchar2(10) path
'//Equipment/#BuildingPractice') x;
This query is working perfectly.But when I am trying to fetch the Cabinet or Subrack details, we are getting below error.
ORA-19279: XPTY0004 - XQuery dynamic type mismatch: expected singleton sequence - got multi-item sequence
ORA-06512: at line 33
19279. 00000 - "XQuery dynamic type mismatch: expected singleton sequence - got multi- item sequence"
*Cause: The XQuery sequence passed in had more than one item.
*Action: Correct the XQuery expression to return a single item sequence.
Top Few Line of XML is given below.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<NetworkInventory xmlns="http://www.ericsson.com/axe/export/hw" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ericsson.com/axe/export/hw file:/opt/ericsson/nms_smo_srv/etc/export.xsd">
<Description>AXE HARDWARE INVENTORY DATA</Description>
<ExportDateTime Date="2015-04-01" Time="17:24:28"/>
<Node AdjustDate="2015-03-21" FunctionType=" " Name="BSC20" Site=" " Type="AXE" UserLabel="">
<Equipment BuildingPractice="BYB501">
<Cabinet Position="CabNumber=1">
<Subrack Name="FAN-1" Position="X=3,Y=2" Type="CP">
<Board Name=" " SlotPosition="255" Type="CP">
<ProductData FirstOperationDate="2013-11-20" LastChangedDate="2013-11-20" ManufacturedDate=" " ProductName=" " ProductNumber=" " ProductRevision=" " SerialNumber=" " Supplier="Ericsson AB"/>
</Board>
<Board Name=" " SlotPosition="255" Type="CP">
<ProductData FirstOperationDate="2013-11-20" LastChangedDate="2013-11-20" ManufacturedDate=" " ProductName=" " ProductNumber=" " ProductRevision=" " SerialNumber=" " Supplier="Ericsson AB"/>
</Board>
</Subrack>
Because your Cabinet is set not a single iterate.
When you want to present repeating groups in relational format, you have to extract the sequence of items in the main XQuery expression.
Each item is then passed to the COLUMNS clause to be further shredded into columns.
You're trying to expand a construct that's sort of similar to nested tables. Your Equipment node can have multiple Cabinets, so to extract details from those you need to pass those to a second XMLTable:
select x.SiteName, x.SiteType, x.BuildingPractice, y.Position
from emp_xml t
cross join xmltable(
xmlnamespaces(default 'http://www.ericsson.com/axe/export/hw'),
'/NetworkInventory/Node' passing t.object_value columns
SiteName varchar2(10) path '#Name',
SiteType varchar2(10) path '#Type',
BuildingPractice varchar2(10) path 'Equipment/#BuildingPractice',
Equipment XMLType path 'Equipment'
) x
cross join xmltable(
xmlnamespaces(default 'http://www.ericsson.com/axe/export/hw'),
'//Cabinet' passing x.Equipment columns
Position varchar2(15) path '#Position'
) y;
SITENAME SITETYPE BUILDINGPRACTICE POSITION
---------- ---------- ---------------- ---------------
BSC20 AXE BYB501 CabNumber=1
To get the the SubRack data too, you'd need to pass that out to a third level of XMLTable, etc.

hive - LATERAL VIEW explode xpath

i have the following input XML:
<?xml version="1.0"?>
<Employees>
<Employee emplid="1111">
<lastname>Watson</lastname>
<age>30</age>
<email>johnwatson#sh.com</email>
</Employee>
<Employee emplid="2222">
<firstname>Sherlock</firstname>
<lastname>Holmes</lastname>
<age>32</age>
<email>sherlock#sh.com</email>
</Employee>
</Employees>
Please notice the firstname missing from the employee 1111
I'm executing following select:
select
c1.emplid,
fname,
lname
from(
select emplid, xmldata from employeeXML
LATERAL VIEW explode (xpath(xmldata,'/Employees/Employee/#emplid')) dummyTable as emplid )c1
LATERAL VIEW explode (xpath(xmldata,concat('/Employees/Employee[#id="',c1.emplid,'"',']/firstname/text()')))dummyTable2 as fname
LATERAL VIEW explode (xpath(xmldata,concat('/Employees/Employee[#id="',c1.emplid,'"',']/lastname/text()'))) dummyTable3
as lname;
The expected result :
1111 NULL Watson
2222 Sherlock Holmes
Please notice that NULL value for the missing first name)
however i'm getting the following result:
2222 Sherlock Holmes
Becasue the first name is missing for the employee 1111, i'm not getting the first employee back in my query.
Is there a way to get both employee data back as indicated in the expected result with first name set to NULL and/or space when it is missing ?
Please help.
thanks,
You can always concatenate the result with an empty string, this should probably be fine:
concat(/Employees/Employee[#id="..."]/firstname/text(), '')
This is not the concatenate you used in hive, but an internal XPath function, so you will probably apply both the XPath and Hive concat in one line.
By the way, I guess you want to use #emplid instead of #id to match your data?

pl-sql include column names in query

A weird request maybe but. My boss wants me to create an admin version of a page we have that displays data from an oracle query in a table.
The admin page, instead of displaying the data (query returns 1 row), needs to return the table name and column name
Ex: Instead of:
Name Initial
==================
Bob A
I want:
Name Initial
============================
Users.FirstName Users.MiddleInitial
I realize I can do this in code but would rather just modify the query to return the data I want so I can leave the report generation code mostly alone.
I don't want to do it in a stored procedure.
So when I spit out the data in the report using something like:
blah blah = MyDataRow("FirstName")
I can leave that as is but instead of it displaying "BOB" it would display "Users.FirstName"
And I want to do the query using select * if possible instead of listing all the columns
So for each of the columns I am querying in the * , I want to get (instead of the column value) the tablename.ColumnName or tablename|columnName
hope you are following- I am confusing myself...
pseudo:
select tablename + '.' + Columnname as WhateverTheColumnNameIs
from Table1
left join Table2 on whatever...
Join Table_Names on blah blah
Whew- after writing all this I think I will just do it on the code side.
But if you are up for it maybe a fun challenge
Oracle does not provide an authentic way(there is no pseudocolumn) to get the column name of a table as a result of a query against that table. But you might consider these two approaches:
Extract column name from an xmltype, formed by passing cursor expression(your query) in the xmltable() function:
-- your table
with t1(first_name, middle_name) as(
select 1,2 from dual
), -- your query
t2 as(
select * -- col1 as "t1.col1"
--, col2 as "t1.col2"
--, col3 as "t1.col3"
from hr.t1
)
select *
from ( select q.object_value.getrootelement() as col_name
, rownum as rn
from xmltable('//*'
passing xmltype(cursor(select * from t2 where rownum = 1))
) q
where q.object_value.getrootelement() not in ('ROWSET', 'ROW')
)
pivot(
max(col_name) for rn in (1 as "name", 2 as "initial")
)
Result:
name initial
--------------- ---------------
FIRST_NAME MIDDLE_NAME
Note: In order for column names to be prefixed with table name, you need to list them
explicitly in the select list of a query and supply an alias, manually.
PL/SQL approach. Starting from Oracle 11g you could use dbms_sql() package and describe_columns() procedure specifically to get the name of columns in the cursor(your select).
This might be what you are looking for, try selecting from system views USER_TAB_COLS or ALL_TAB_COLS.

Resources