PostgreSQL11 xpath query not working properly - sql

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')

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.

How to extract this XML

I want to extract a xml file (XML_DATA):
The XML:
-<XP6>
+<INFO_1>
+<INFO_2>
+<INFO_3>
-<Prdct>
-<Prdct_row>
.....
<LILBFLO>Samsung,corp. </LILBFLO> <--value
I tried this, but it's not working:
EXTRACTVALUE(XML_DATA,'/Prdct/Prdct_row/LILBFLO/text()')
How to use extractvalue correctly?
Assuming the +/- symbols indicate collapsed nodes and your XML actually looks something like the sample in this CTE, you just need to include the root node in the path:
with your_table (xml_data) as (
select xmltype('<XP6>
<INFO_1/>
<INFO_2/>
<INFO_3/>
<Prdct>
<Prdct_row>
<LILBFLO>Samsung,corp. </LILBFLO>
</Prdct_row>
</Prdct>
</XP6>') from dual
)
select EXTRACTVALUE(XML_DATA,'/XP6/Prdct/Prdct_row/LILBFLO/text()')
from your_table;
EXTRACTVALUE(XML_DATA,'/XP6/PRDCT/PRDCT_ROW/LILBFLO/TEXT()')
------------------------------------------------------------
Samsung,corp.
But the extractvalue() function is deprecated, so you should use an XMLQuery instead:
select XMLQuery('/XP6/Prdct/Prdct_row/LILBFLO/text()' passing XML_DATA returning content)
from your_table;
XMLQUERY('/XP6/PRDCT/PRDCT_ROW/LILBFLO/TEXT()'PASSINGXML_DATARETURNINGCONTENT)
--------------------------------------------------------------------------------
Samsung,corp.

Oracle 10.2.0.4.0 query on partial xpath

I need to change the below query to be able to query any kind of tender item.
/Basket/CardTenderItem/Description
/Basket/CashTenderItem/Description
So
/Basket/WildcardTenderItem/Description
I have looked at various examples on but cannot them to bring back any results when running (happily admit to user error if can get working!)
SELECT
RETURN_ID
,SALE_ID,
,extractValue(xmltype(RETURNxml),'/Basket/CashTenderItem/NetValue')
,extractValue(xmltype(RETURNxml),'/Basket/CashTenderItem/Description')
FROM SPR361
WHERE return_id = '9999.0303|20170327224954|2063'
If you only want to match anything the ends with TenderItem, but doesn't have anything after that, you could be specific with substring checks:
SELECT
RETURN_ID
,SALE_ID
,extractValue(xmltype(RETURNxml),
'/Basket/*[substring(name(), string-length(name()) - 9) = "TenderItem"]/NetValue')
,extractValue(xmltype(RETURNxml),
'/Basket/*[substring(name(), string-length(name()) - 9) = "TenderItem"]/Description')
FROM SPR361
WHERE return_id = '9999.0303|20170327224954|2063'
If you never have any nodes with anything after that fixed string then #Shnugo's contains approach is easier, and in Oracle would be very similar:
...
,extractValue(xmltype(RETURNxml),
'/Basket/*[contains(name(), "TenderItem")]/NetValue')
,extractValue(xmltype(RETURNxml),
'/Basket/*[contains(name(), "TenderItem")]/Description')
I'm not sure there's any real difference between name() and local-name() here.
If a basket can have multiple child nodes (card and cash, or more than one of each) you could also switch to XMLTable syntax:
SELECT
s.RETURN_ID
,s.SALE_ID
,x.netvalue
,x.description
FROM SPR361 s
CROSS JOIN XMLTable(
'/Basket/*[contains(name(), "TenderItem")]'
PASSING XMLType(s.RETURNxml)
COLUMNS netvalue NUMBER PATH './NetValue'
, description VARCHAR(80) PATh './Description'
) x
WHERE s.return_id = '9999.0303|20170327224954|2063'
And it's overkill here maybe, but for more complicated tests you can use other XPath syntax, like:
CROSS JOIN XMLTable(
'for $i in /Basket/*
where contains($i/name(), "TenderItem") return $i'
PASSING XMLType(s.RETURNxml)
...
This is SQL-Server syntax and I cannot test, if this works with Oracle too, but I think it will. You can use XQuery function contains():
DECLARE #xml XML=
N'<root>
<abcTenderItem>test1</abcTenderItem>
<SomeOther>should not show up</SomeOther>
<xyzTenderItem>test2</xyzTenderItem>
</root>';
SELECT #xml.query(N'/root/*[contains(local-name(),"TenderItem")]')
only the elements with "TenderItem" in their names show up:
<abcTenderItem>test1</abcTenderItem>
<xyzTenderItem>test2</xyzTenderItem>

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

Issue with creating XMLQuery for given XPATH

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(.)