Issue with creating XMLQuery for given XPATH - sql

I have a table having one columns as XMLTYPE being stored with Object-Relational storage. Below is table ddl.
CREATE TABLE Orders ( Order_id number not null,
Order_status Varchar2(100),
Order_desc XMLType not null)
XMLTYPE Order_desc STORE AS OBJECT RELATIONAL
XMLSCHEMA "http://localhost/public/xsd/Orderstore.xsd"
ELEMENT "OrderVal"
);
I have successfully registered the schema to load XSD with XML DB. Below is the XML being loaded into the XMLTYPE column.
<?xml version="1.0" encoding="utf-8" ?>
<draftorders>
<OrderSumm>
<Ordercod>OrderBookings</Ordercod>
</OrderSumm>
<Orderattrs>
<Orderattr Ordername="HROrder">
<OrderVals>
<OrderVal>
<listvalue>Order1</listvalue>
<Orderattrs>
<Orderattr Ordername="Node1_Child1">
<OrderVals>
<OrderVal>
<listvalue><![CDATA[ Node1_Child1_OrderValue_1]]></listvalue>
<Orderattrs>
<Orderattr Ordername="Node2_Child1">
<OrderVals>
<OrderVal>
<listvalue><![CDATA[ Node2_Child1_OrderValue_1]]></listvalue>
</OrderVal>
</OrderVals>
</Orderattr>
<Orderattr Ordername="Node2_Child2">
<OrderVals>
<OrderVal>
<listvalue><![CDATA[ Node2_Child2_OrderValue_1]]></listvalue>
</OrderVal>
</OrderVals>
</Orderattr>
</Orderattrs>
</OrderVal>
</OrderVals>
</Orderattr>
</Orderattrs>
</OrderVal>
</OrderVals>
</Orderattr>
</Orderattrs>
</draftorders>
I have the query using "extract" to print the below output:
SELECT extract(o.Order_desc,'/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/Orderattrs/Orderattr[0]/#Ordername').getStringVal() "Node1",
extract(o.Order_desc,'/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/Orderattrs/Orderattr[0]/OrderVals/OrderVal[1]/listvalue/text()').getStringVal() "Node1Child",
extract(o.Order_desc,'/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/Orderattrs/Orderattr[1]/#Ordername').getStringVal() "Node2",
extract(c.Order_desc,'/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/listvalue/text()').getStringVal() "Node2Child"
FROM Orders o;
OUTPUT:-
Node2_Child1
Node2_Child1_OrderValue_1
Node2_Child2
Node2_Child2_OrderValue_1
I want to achieve the same output using XMLQuery, but I am unable to build query to print the child node. Till now, I can only print the node value using XMLQuery as given below:-
SELECT XMLQuery( '/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/Orderattrs/Orderattr[0]/#Ordername'
PASSING o.Order_desc RETURNING CONTENT
)
FROM Orders o;
How can I achieve the same output from using "extract", with "XMLQuery" ?
Thanks.
/********
Modified query run:-
SELECT XMLQuery('//OrderVal/Orderattrs/Orderattr/(#Ordername, OrderVals/OrderVal/listvalue)/data(.)'
PASSING o.Order_desc RETURNING CONTENT
)
FROM Orders o;
Output:-
Node2_Child1
Node2_Child1_OrderValue_1
Node2_Child
Fetching all Nodes and its child's using XMLTABLE.
SELECT ord.OrdName, ord.OrdVal
FROM Orders, XMLTable('/OrderVal[1]/Orderattrs/Orderattr[1]/OrderVals/OrderVal[1]/Orderattrs/Orderattr'
PASSING Order_desc
COLUMNS "OrdName" VARCHAR2(4000) PATH '#Ordername',
"OrdVal" VARCHAR2(4000) PATH 'OrderVals/OrderVal[1]/listvalue') ord;
Output:-
Node2_Child1
Node2_Child1_OrderValue_1
Node2_Child2
Node2_Child2_OrderValue_1
......
Node2_Child2500
Node2_Child2500_OrderValue_1
How can I achieve the same using XMLQuery ??

You queries and example XML do not seem to match, so I aligned to your queries as far as possible. As your XML was broken, too you might have to adjust some axis steps.
You can "branch" at some point (for example to fetch the attribute value and a subnode) using parentheses. This query will fetch all results at once for an arbitrary number of order attributes:
//OrderVal/Orderattrs/Orderattr/(#Ordername, OrderVals/OrderVal/listvalue)/data(.)

Related

Unable to pull the results by passing input as xml path in oracle

I am just trying to pull the records bypassing the XML path in where the condition of SQL unfortunately I am unable to do.
Note: Pull the records bypassing input as salesteamid value. Please someone can help me to run the query without any failures.
Query :
SELECT * from activity where= ExtractValue('/ActivityId/Agent/Territory/SalesTeamId[contains(10669)]');
XML:
<Activity
xmlns="urn:astrazeneca:na:Activity:domain:3" RestrictionGroup="NONE"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="DiscussionActivityType" TransactionCode="I" CreatedOnDate="2019-11-21T16:04:29" UpdatedOnDate="2019-11-21T16:04:32" SourceCreatedByID="20303090" SourceCreatedByFirstName="Brandy" SourceCreatedByLastName="Nirider" SourceCreatedByRole="" SourceUpdatedByID="20303090" SourceUpdatedByFirstName="Brandy" SourceUpdatedByLastName="Nirider" SourceSystemCode="FSA" SystemOfRecordCode="FSA" RecordCompanyCode="AZN" SourceCountry="USA" SourceRegion="USA" SourceSystemGroup="VNA" SystemOfRecordGroup="VNA">
<ActivityId>
<ns2:ID
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2" SystemCode="VNA">a080P00001YZM7WQAX
</ns2:ID>
</ActivityId>
<Agent>
<EmployeeId>
<ns2:ID
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2" SystemCode="EMPLID">20303090
</ns2:ID>
</EmployeeId>
<Territory>
<ns2:TerritoryId
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2">
<ns2:ID SystemCode="AZ">20070009</ns2:ID>
</ns2:TerritoryId>
<ns2:TerritoryCode
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2">20070009
</ns2:TerritoryCode>
<ns2:Role
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2">PSS
</ns2:Role>
<ns2:Description
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2">Indianapolis C IN 2
</ns2:Description>
<ns2:SalesTeamId
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2">
<ns2:ID SystemCode="AZ">10669</ns2:ID>
</ns2:SalesTeamId>
<ns2:SalesTeamCode
xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2">D_ALPHA
</ns2:SalesTeamCode>
</Territory>
</Agent>
<Customer>
<AZCustomerMasterID SystemCode="AZ">58586509</AZCustomerMasterID>
<SourceCustomerID SystemCode="NAVVA">001U000000pMTySIAW</SourceCustomerID>
<CustomerType>HCP</CustomerType>
<CustomerActivityRole>participant</CustomerActivityRole>
</Customer>
<ReferenceContent>
<ReferenceName>Reported Adverse Reaction</ReferenceName>
<ReferenceType>action</ReferenceType>
<ReferenceId SystemCode="VNA">Reported Adverse Reaction</ReferenceId>
</ReferenceContent>
<StartDate>2019-11-21T16:03:00</StartDate>
<EndDate>2019-11-21T16:03:00</EndDate>
<Topic>
<Name>BYDUREON BCISE</Name>
<Product>
<AZBrandId SystemCode="AZ">881574</AZBrandId>
<AZProductId SystemCode="AZ">881574</AZProductId>
<SourceProductId SystemCode=""/>
<ReferenceProductId SystemCode=""/>
<ProductName>BYDUREON BCISE</ProductName>
</Product>
</Topic>
<ActivityAction>discussion</ActivityAction>
<ActivityStatus>completed</ActivityStatus>
<ActivityInitiatedBy>AstraZeneca agent</ActivityInitiatedBy>
<ActivityOutcome>success with identified customer</ActivityOutcome>
<CommunicationMode>in person</CommunicationMode>
<LocationSetting>Selling</LocationSetting>
<SourceValues>
<Status>Submitted</Status>
<SubStatus>Completed</SubStatus>
<Applet>Actions</Applet>
<Activity>Detail Only</Activity>
<InteractionCategory>A_Selling (PSS)</InteractionCategory>
<Type>Selling</Type>
</SourceValues>
<Priority>2</Priority>
<DiscussionTool>N</DiscussionTool>
<ActivityTypeCheck>DiscussionActivityType</ActivityTypeCheck>
</Activity>
Assuming you're trying to filter on an ID within the XML, you have an issue with syntax, you've supplied the root node as ActivityId instead of Activity, you aren't going down to the ID node, you're misusing contains - and that probably isn't what you want anyway; and you're ignoring the namespaces.
This will only find rows where the XML has a specific ID:
SELECT * from activity
where ExtractValue(xml,
'/Activity/Agent/Territory/ns2:SalesTeamId/ns2:ID/text()',
'xmlns="urn:astrazeneca:na:Activity:domain:3" xmlns:ns2="urn:astrazeneca:na:CommonTypes:domain:2"'
) = 10669;
But ExtractValue has long been deprecated, so you should use XMLQuery instead. Or in this context it would probably make more sense to use XMLExists, with the value you want embeded:
SELECT * from activity
where XMLExists('
declare default element namespace "urn:astrazeneca:na:Activity:domain:3";
declare namespace ns2="urn:astrazeneca:na:CommonTypes:domain:2";
/Activity/Agent/Territory/ns2:SalesTeamId/ns2:ID[text()=10669]
'
passing xml
);
or perhaps more usefully supplied as an argument:
SELECT * from activity
where XMLExists('
declare default element namespace "urn:astrazeneca:na:Activity:domain:3";
declare namespace ns2="urn:astrazeneca:na:CommonTypes:domain:2";
/Activity/Agent/Territory/ns2:SalesTeamId/ns2:ID[text()=$id]
'
passing xml, 10669 as "id"
);
db<>fiddle
If you then want to extract specific data from the XML in matching rows then look at XMLQuery or XMLTable - but that's a separate issue.

Oracle XML query returns NULL

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

Sum of values extracted using SQL

I have an xml like the below.
<LPNDetail>
<ItemName>5054807025389</ItemName>
<DistroNbr/>
<DistributionNbr>TR001000002514</DistributionNbr>
<OrderLine>2</OrderLine>
<RefField2/>
<RefField3>OU01180705</RefField3>
<RefField4>0002</RefField4>
<RefField5>Retail</RefField5>
<Qty>4</Qty>
<QtyUom>Unit</QtyUom>
</LPNDetail>
<LPNDetail>
<ItemName>5054807025563</ItemName>
<DistroNbr/>
<DistributionNbr>TR001000002514</DistributionNbr>
<OrderLine>4</OrderLine>
<RefField2/>
<RefField3>OU01180705</RefField3>
<RefField4>0004</RefField4>
<RefField5>Retail</RefField5>
<Qty>2</Qty>
<QtyUom>Unit</QtyUom>
</LPNDetail>
I have extracted the xml field using extract.xmltype and now i am getting the below result.
42
But i need to sum the quantity values i.e i need to get result as 6 (4+2).
Any help will be appreciated.
Thanks,
Shihaj
It is not clear what you mean by "an xml". If it's supposed to be an XML document, you are missing the outermost tags, perhaps something like <Document> ..... </Document>
If your text value is EXACTLY as you have shown it (which would be pretty bad), you can wrap within such outermost tags manually, and then use standard Oracle XML tools. For the illustration below I assume you simply have a string (VARCHAR2 or CLOB), not converted to XML type; in that case, I concatenate the beginning and end tags, and then convert to XMLtype, in the query.
with t ( str ) as (
select '<LPNDetail>
<ItemName>5054807025389</ItemName>
<DistroNbr/>
<DistributionNbr>TR001000002514</DistributionNbr>
<OrderLine>2</OrderLine>
<RefField2/>
<RefField3>OU01180705</RefField3>
<RefField4>0002</RefField4>
<RefField5>Retail</RefField5>
<Qty>4</Qty>
<QtyUom>Unit</QtyUom>
</LPNDetail>
<LPNDetail>
<ItemName>5054807025563</ItemName>
<DistroNbr/>
<DistributionNbr>TR001000002514</DistributionNbr>
<OrderLine>4</OrderLine>
<RefField2/>
<RefField3>OU01180705</RefField3>
<RefField4>0004</RefField4>
<RefField5>Retail</RefField5>
<Qty>2</Qty>
<QtyUom>Unit</QtyUom>
</LPNDetail>'
from dual
)
-- End of SIMULATED table (for testing purposes only, not part of the solution)
-- Query begins below this line
select sum(x.qty) as total_quantity
from t,
xmltable('/Document/LPNDetail'
passing xmltype('<Document>' || t.str || '</Document>')
columns qty number path 'Qty') x
;
Output:
TOTAL_QUANTITY
--------------
6

Getting XML out of a table created in SQL

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

postgresql : xml parsing : xpath return only one record, where is xml contains 3 records

I have a procedure in postgresql with xml type paramter.
I need to parse xml and insert record in a table.
I tried this way, but it is inserting only one record in table, where as xml contains three records.
the xml code is
Select (xpath('/ArrayOfUserGroupModulePermissionBusinessObject/UserGroupModulePermissionBusinessObject/ModuleID/text()', t1))[1]::varchar::BIGINT AS ModuleID
from (Select Perms::xml as t1) as t2;
the xml is here
<ArrayOfUserGroupModulePermissionBusinessObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<UserGroupModulePermissionBusinessObject>
<UserGroupModulePermissionID>0</UserGroupModulePermissionID>
<UserGroupID>0</UserGroupID>
<ModuleID>3</ModuleID>
<ModuleName>Administration</ModuleName>
<CanRead>false</CanRead>
<CanAdd>false</CanAdd>
<CanEdit>false</CanEdit>
<CanDelete>false</CanDelete>
<CanPrint>false</CanPrint>
</UserGroupModulePermissionBusinessObject>
<UserGroupModulePermissionBusinessObject>
<UserGroupModulePermissionID>0</UserGroupModulePermissionID>
<UserGroupID>0</UserGroupID>
<ModuleID>2</ModuleID>
<ModuleName>Data Management</ModuleName>
<CanRead>false</CanRead>
<CanAdd>false</CanAdd>
<CanEdit>false</CanEdit>
<CanDelete>false</CanDelete>
<CanPrint>false</CanPrint>
</UserGroupModulePermissionBusinessObject>
<UserGroupModulePermissionBusinessObject>
<UserGroupModulePermissionID>0</UserGroupModulePermissionID>
<UserGroupID>0</UserGroupID>
<ModuleID>1</ModuleID>
<ModuleName>Reception</ModuleName>
<CanRead>false</CanRead>
<CanAdd>false</CanAdd>
<CanEdit>false</CanEdit>
<CanDelete>false</CanDelete>
<CanPrint>false</CanPrint>
</UserGroupModulePermissionBusinessObject>
</ArrayOfUserGroupModulePermissionBusinessObject>
I don't know what is wrong in this sql
The xpath function returns an array with an element for each result. You only use the first element in the array while the array has actually all three elements you expect. Use unnest to convert the array to rows.
Select unnest(xpath('/ArrayOfUserGroupModulePermissionBusinessObject/UserGroupModulePermissionBusinessObject/ModuleID/text()', t1))::varchar::BIGINT AS ModuleID
from (Select Perms::xml as t1) as t2;