I'm querying an Oracle SQL table containing XML.
The (simplified) XML structure is as follows:
<aggregate type="HeadAggregate">
<entity type="Default" root="true" id="asdb7e9e-93324-43242d-b83a-f2d3202ed">
<attribute name="ObjectName" multivalue="false">ExampleName</attribute>
<attribute name="Subprocesses" multivalue="false">false</attribute>
<attribute name="ObjectDesc" multivalue="false">Description</attribute>
</entity>
<aggregate>
I want to get to retreive the object name. Therefore I wrote the following query:
SELECT xt.*
FROM DATABASENAME.TABLENAME x,
XMLTABLE('/aggregate/entity[#type = ''Default'']'
PASSING XMLTYPE(x.DATA)
COLUMNS
attribute_name VARCHAR2(100) PATH 'attribute[1]'
) xt
So far so good. This works fine! I get the desired output.
However, now I want to replace '[1]' in PATH by a reference to the attribute name to make my script a bit more flexible.
So I changed my script to:
SELECT xt.*
FROM DATABASENAME.TABLENAME x,
XMLTABLE('/aggregate/entity[#type = ''Default'']'
PASSING XMLTYPE(x.DATA)
COLUMNS
attribute_name VARCHAR2(100) PATH 'attribute[#name = ''ObjectName'']'
) xt
For some reason, now I get the following error message:
00907. 00000 - "missing right parenthesis"
*Cause:
*Action:
Error at Line: 3 Column: 47
I've been struggling on this for a while.
What am I doing wrong?
Many thanks in advance!
You can (or should) use double-quotes, rather than escaped single-quotes:
SELECT xt.*
FROM DATABASENAME.TABLENAME x,
XMLTABLE('/aggregate/entity[#type = "Default"]'
PASSING XMLTYPE(x.DATA)
COLUMNS
attribute_name VARCHAR2(100) PATH 'attribute[#name = "ObjectName"]'
) xt
db<>fiddle
Related
I need help with the following.
I have a table which has one of the columns as type BLOB. Executing following query allows me to read the BLOB in the Oracle SQL developor's text editor as the image below:
select
utl_raw.cast_to_varchar2(utl_compress.lz_uncompress(a.DATA))
from my_table a where a.id = 11266392;
I can double click on above shaded cell and read it in text. This is nice but it only works for one row at a time. My actual goal is to read specific data within xml tags from each of these BLOBs and there are roughly a million of those for each month.
I thought i could cast this into a string and read the first 4000 characters but that was useless as my BLOBs for each record are of length 400K (using getlength procedure from dbms_lob).
I tried casting the blob in XML via this
select CONVERT(xml,a.data, 2) from
(select utl_compress.lz_uncompress(a.DATA) as data from my_table a where a.id = 11266392) a;
But this threw an error:
ORA-00904: "XML": invalid identifier
00904. 00000 - "%s: invalid identifier"
*Cause:
*Action:
Error at Line: 10 Column: 16
My question is then, is there a way I can use extract XML, XQuery or XMLTable on the blob itself (after I have applied the lz_uncompress function on it)?
What are my other alternatives?
Here is the heavily edited XML in one of the blob (note that I have about 10-15 such Worksheet tags)
<Worksheets>
<Worksheet Description="Some Coverage" EffectiveDate="2020-06-28T00:01:00-05:00" ExpirationDate="2021-06-28T00:01:00-05:00" FixedId="Table:13263928">
<Routine RateBookCode="XX" RateBookEdition="00006" RoutineCode="XXX" RoutineVersion="1">
<Store Declaration="true" Result="false" ResultType="java.lang.Boolean" Variable="hasSpecialLimits">
<PropertyGet ObjectName="XXX" ObjectType="XXX" ObjectValue="My Address" PropertyName="HasSpecialLimits" Type="XXXXX" Value="false" ValueType="java.lang.Boolean"/>
</Store>
</Routine>
</Worksheet>
</Worksheets>
It should work like this:
SELECT XMLTYPE( a.data, 2 )
FROM ( select utl_compress.lz_uncompress(a.DATA) as data
from my_table a where a.id = 11266392 ) a;
When I execute below query in Postgres 10.12, it works properly.
SELECT (xpath('./sid/text()', c.node))[1]::text::bigint AS STUDENT_ID,
(xpath('./name/text()', c.node))[1]::text AS STUDENT_NAME
from (
select unnest(xpath('/data', '<data><sid>112233</sid><name>John</name></data>'::xml)) AS node
) c;
Output:
But When I execute same query in Postgres 11.7, it is not working.
What is the solution to fix this issue?
This is caused by this change:
Correctly handle relative path expressions in xmltable(), xpath(), and other XML-handling functions (Markus Winand)
Per the SQL standard, relative paths start from the document node of the XML input document, not the root node as these functions previously did.
so you need to change it to:
SELECT (xpath('/data/sid/text()', c.node))[1]::text::bigint AS STUDENT_ID,
(xpath('/data/name/text()', c.node))[1]::text AS STUDENT_NAME
from (
select unnest(xpath('/data', '<data><sid>112233</sid><name>John</name></data>'::xml)) AS node
) c;
because the inner xpath will return the <data> tag as well:
select unnest(xpath('/data', '<data><sid>112233</sid><name>John</name></data>'::xml)) AS node
results in:
<data>
<sid>112233</sid>
<name>John</name>
</data>
However, I would use xmltable for this:
select *
from xmltable('/data'
passing xml('<data><sid>112233</sid><name>John</name></data>')
columns
student_id bigint path 'sid',
student_name text path 'name')
I need to convert XML data to field/value pair but my query for the value is returning nulls
I have an XML type column in Oracle that I am trying to parse to get the field/value pair. I'm not sure if the structure of the XML is the cause but I cannot extract the value.
Here is a sample of the XML data:
<SaleFile BRANCH_ID="ABCZ7" name="FILE20190523_BN0016_MAIN.DAT" File_Type="MAIN" id="574455888ABCYY7" type="ACCOUNT">
<FIELD Type="ACCOUNT BALANCES AND CORRESPONDING DATES" Redact_ind="FALSE" Name="POSTED_PMT_AMT">-673.00</FIELD>
<FIELD Type="CONSUMER CONTACT INFORMATION" Redact_ind="FALSE" Name="SECONDARY CUSTOMER">N</FIELD>
<FIELD Type="CONSUMER IDENTIFYING INFORMATION" Redact_ind="FALSE" Name="SECONDARY CUSTOMER NAME"></FIELD>
</SaleFile>
I am trying to get the posted amount returned in my query.
SELECT xt.*
FROM SALES_DATA x,
XMLTABLE('/SaleFile/FIELD'
PASSING x.SELLER_DATA
COLUMNS
FIELD_VAL VARCHAR2(500) PATH 'FIELD'
) xt
The query runs without error but all I get back are NULLs.
Consider a different approach with xmlSequence and extractValue() with conditional in XPath expression:
select
extractValue(value(e), '//FIELD[#Type=''ACCOUNT BALANCES AND CORRESPONDING DATES'']') AS "amount"
from SALES_DATA x,
table (xmlSequence(extract(xml, '/SaleFile'))) e
Rextester Demo
Figured out by just trying some different codes:
SELECT xt.* FROM SALES_DATA x,
XMLTABLE('/SaleFile/FIELD'
PASSING x.Seller_DATA
COLUMNS
FIELD_VAL VARCHAR2(500) PATH '/' ) xt
this gave me the list of values for each FIELD node.
You have few FIELD elements, so a filter based on the Name="POSTED_PMT_AMT" attribute will be helpful.
SQL
SELECT xt.*
FROM SALES_DATA x,
XMLTABLE('/SaleFile/FIELD[#Name="POSTED_PMT_AMT"]'
PASSING x.SELLER_DATA
COLUMNS
PMT_AMT NUMERIC(10,2) PATH './text()'
) xt
I am trying to select a specific xml value as column in a Oracle 11G table which is stored as XML - Huge CLOB, but unable to. Kindly help
Contents of XML as Below
<Bid xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/LCC.Crew.FAReserves.wsvc.Entities.FAReserves">
<AggressiveBidType></AggressiveBidType>
<BidCriteria>
<BidCriteria i:type="RapBidCriteria">
<Value xmlns:d4p1="http://www.w3.org/2001/XMLSchema" i:type="d4p1:string">BAC</Value>
</BidCriteria>
</BidCriteria>
<BidItem>RAP</BidItem>
<BidName>BAC</BidName>
<BidType>Standing</BidType>
<CatsId>10023</CatsId>
<EmployeeBidId>10620</EmployeeBidId>
<EmployeeId>135289</EmployeeId>
<EndDate>2015-03-29T00:00:00Z</EndDate>
<IsAggressive>false</IsAggressive>
<IsLodo>false</IsLodo>
<IsOnPremiseReserve>false</IsOnPremiseReserve>
<OperatingDate>2015-02-25T00:00:00Z</OperatingDate>
<Priority>0</Priority>
</Bid>
Below Statement return null
SELECT extract(XMLTYPE(XMLBIDCONTENT),'/Bid/BidName/text()') "REFERENCE"
FROM AOCREWBIDDING.EMPLOYEEBIDS
Where EmployeeBidID = 100
Below statement returns error
ORA-00932: inconsistent datatypes: expected - got -
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*Cause:
*Action: Error at Line: 83 Column: 8
SELECT extract(XMLBIDCONTENT,'/Bid/BidName/text()').getStringVal() "REFERENCE"
FROM AOCREWBIDDING.EMPLOYEEBIDS
Where EmployeeBidID = 100
The extract() function is deprecated. It's better to use XMLQuery().
You need to either declare a default namespace to match the one in the XML document:
select XMLQuery('
declare default element namespace
"http://schemas.datacontract.org/2004/07/LCC.Crew.FAReserves.wsvc.Entities.FAReserves"; (: :)
/Bid/BidName/text()'
passing XMLType(xmlbidcontent)
returning content) as BidName
from employeebids
where EmployeeBidID = 100;
BIDNAME
--------------------------------------------------------------------------------
BAC
or (simpler but less robust) use a wildcard:
select XMLQuery('/*:Bid/*:BidName/text()'
passing XMLType(xmlbidcontent)
returning content) as BidName
from employeebids
where EmployeeBidID = 100;
BIDNAME
--------------------------------------------------------------------------------
BAC
db<>fiddle showing your original queries and both of these, using a CTE to provide the sample CLOB value.
I have created this table in Oracle SQL Developer
CREATE TABLE Test_T (
COL_1 VARCHAR(30),
COL_2 XMLTYPE
);
And have inserted this into it
INSERT INTO Test_T VALUES ('two', ('<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
<PLANT>
<COMMON>Bloodroot</COMMON>
<BOTANICAL>Sanguinaria canadensis</BOTANICAL>
<ZONE>4</ZONE>
<LIGHT>Mostly Shady</LIGHT>
<PRICE>$2.44</PRICE>
<AVAILABILITY>031599</AVAILABILITY>
</PLANT>
<PLANT>
<COMMON>Columbine</COMMON>
<BOTANICAL>Aquilegia canadensis</BOTANICAL>
<ZONE>3</ZONE>
<LIGHT>Mostly Shady</LIGHT>
<PRICE>$9.37</PRICE>
<AVAILABILITY>030699</AVAILABILITY>
</PLANT></CATALOG>'));
My goal is to return the <COMMON> Name </COMMON> ONLY WHERE the zone is 3 or LESS. So this should return Columbine.
I thought about using XMLExists Im not too familiar with XML so this is what I had so far.
SELECT COL_2 FROM Test_T WHERE XMLExists('//ZONE[COL_2 <= 3]' PASSING BY REF COL_2);
I'm not sure if I am accessing the ZONE right.
Could anyone guide me in the right direction?
Try the below select query :
SELECT COMMON_NAME FROM Test_T WHERE XMLExists( 'CATALOG/PLANT[ZONE<=3]/COMMON[text()]' PASSING COMMON_NAME )
The problem is with your path( '//ZONE[COL_2 <= 3]' ). COL_2 is not a valid XML node, it's just the name of your column.
The proper path would be //ZONE[text() <= 3].
text() is a special node reference that tells oracle to grab the text inside the ZONE node <ZONE>THIS TEXT</ZONE>. You can only target nodes in your actual XML schema.
Also, be aware that the path is CASE SENSITIVE to what's in your XML. Remembering that will save you time.
Additionally, another way to write your select would be this. In this example, Oracle does an implicit join and returns a row for each //PLANT with /ZONE/text() <= 3. The path in the XMLSEQUENCE is important here because it determines how oracle splits up each row, meaning that you can't just target //ZONE because you would only get a row for each ZONE rather than a row for each PLANT.
In the select clause, you can extract individual node values for each PLANT if you have more than one.
SELECT VALUE(P) --THE FULL XML FOR EACH PLANT
VALUE(P).EXTRACT('//COMMON/text()').getstringval() AS COMMON, --INDIVIDUAL NODE VALUE
VALUE(P).EXTRACT('//BOTANICAL/text()').getstringval() AS BOTANICAL --INDIVIDUAL NODE VALUE
FROM Test_T, TABLE(XMLSEQUENCE(EXTRACT(COL_2, '//PLANT[ZONE<= 3]'))) p