Finding the relevant records using XQuery/XPath - sql

I'm very very new to XQUERY/XPATH :) so I could very well be going about this the wrong way. I have a customer object serialized and stored in a database column in the following format.
<Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Addresses>
<AddressBlock>
<AddressLine1>1234 SomeStreet Ave.</AddressLine1>
<City>SomeCity</City>
<State>SomeState</State>
<Zipcode>SomeZip</Zipcode>
</AddressBlock>
<AddressBlock>
<AddressLine1>5678 SomeOtherStreet Ave.</AddressLine1>
<City>SomeOtherCity</City>
<State>SomeOtherState</State>
<Zipcode>SomeOtherZip</Zipcode>
</AddressBlock>
</Addresses>
</Customer>
I'm looking for a way to select this record if addressline1 and city in the same addressblock contains certain keywords. I have the following statement that almost does what I'm looking for.
select *
from users
where [UserData].exist('/Customer/Addresses/AddressBlock/AddressLine1/text()[contains(upper-case(.),""SOMESTREET"")]')=1
and [UserData].exist('/Customer/Addresses/AddressBlock/City/text()[contains(upper-case(.),""SOMECITY"")]')=1"
My only problem is this statment will also return the record if the first addressblock contains the addressline1 and the second addressblock contains the city.

You have to test both conditions in the same XQuery.
select *
from users
where [UserData].exist('/Customer/Addresses/AddressBlock
[contains(upper-case(AddressLine1[1]),"SOMESTREET") and
contains(upper-case(City[1]),"SOMECITY")]')=1

Related

i want write a sql query to fetch data between two tags in soap xml which present in the clob column of a oracle table

xml which is present ina clob column of xml_message table :
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:oag="http://gateway.consumerhub.commercial.volvocars.net/oagis" xmlns:oag1="http://www.openapplications.org/oagis" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<oag:UpdateConsumerAndOpportunityData_v1 xmlns:ns3="http://www.ford.com/oagis">
<oag1:ApplicationArea>
<oag1:Sender>
<oag1:LogicalId>Volvo</oag1:LogicalId>
<oag1:Component>Vista</oag1:Component>
<oag1:Task>SyncConsumer</oag1:Task>
<oag1:AuthorizationId>AUTH-SK034</oag1:AuthorizationId>
</oag1:Sender>
<oag1:CreationDateTime>2016-01-27T15:07:47Z</oag1:CreationDateTime>
<oag1:BODId>sk-connect-034</oag1:BODId>
</oag1:ApplicationArea>
<oag:DataArea>
<oag1:Sync confirm="Always">
<oag1:SyncCriteria expressionLanguage="XPath">
<oag1:SyncExpression action="change"/>
</oag1:SyncCriteria>
</oag1:Sync>
<oag:Consumer>
<oag:Header>
<oag:Source>AU</oag:Source>
</oag:Header>
<oag:ConsumerDetails Category="Person">
<oag:ConsumerId/>
<oag:Language>EN</oag:Language>
<oag:PersonDetails>
<oag:FirstName>firstname_email_034</oag:FirstName>
<oag:Surname>sname_email_034</oag:Surname>
</oag:PersonDetails>
<oag:EmailAddress Active="true">ticc888#yahoo.com</oag:EmailAddress>
<oag:EmailAddress Active="true" Usage="Business">ahng2#hotmail.com</oag:EmailAddress>
<oag:EmailAddress Active="true" Usage="Invoice">jsmith#gmail.com</oag:EmailAddress>
</oag:ConsumerDetails>
</oag:Consumer>
</oag:DataArea>
</oag:UpdateConsumerAndOpportunityData_v1>
</soapenv:Body>
</soapenv:Envelope>
Now i want all the email address from this xml to fetch and insert into another table.
this xml contain 3 email address ,i want till n number of email address to be fetched that a xml file will contain
As the linked answer shows, you can use XMLTable to extract multiple values; but you need to define at least the namespaces you use, and construct the XPath properly. So to get just the email addresses you can do:
select x.emailaddress
from xml_message m
cross apply xmltable (
xmlnamespaces (
'http://schemas.xmlsoap.org/soap/envelope/' as "soapenv",
'http://gateway.consumerhub.commercial.volvocars.net/oagis' as "oag",
'http://www.openapplications.org/oagis' as "oag1",
'http://www.ford.com/oagis' as "ns3"
),
'soapenv:Envelope/soapenv:Body/oag:UpdateConsumerAndOpportunityData_v1/oag:DataArea/oag:Consumer/oag:ConsumerDetails/oag:EmailAddress'
passing xmltype(m.xml_clob)
columns
EmailAddress varchar2(64) path '.'
) x;
EMAILADDRESS
ticc888#yahoo.com
ahng2#hotmail.com
jsmith#gmail.com
The XPath doesn't refer to oag1 or ns3 but it doesn't hurt to include them anyway, and you might need them to extract other information from the same XML document.
This also lets you extract multiple values at once; or to filter base on an attribute - you might, for example, only want to include the email addresses with attribute Active set to true, which you can do by adding that condition to the XPath:
'soapenv:Envelope/soapenv:Body/oag:UpdateConsumerAndOpportunityData_v1/oag:DataArea/oag:Consumer/oag:ConsumerDetails/oag:EmailAddress[#Active="true"]'
db<>fiddle
You can read more about manipulating XML in the documentation.

Query XML data from a table - Use schema collection?

I need to re-write an old DTS/SQL2000 process that exports xml data stored as a varchar from a table to an xml file only to read it back and populate some tables such as contract/customer/business activity. I want to query the xml column directly instead but always get Blank or NULL depending on the method I use and I am having a really having a hard time. My latest quick version based on another answer is....
SELECT
XML_MESSAGE.value('(Transaction_Id/text())[1]','varchar(100)') AS TransactionID
FROM dbo.XML_REPOSITORY t
OUTER APPLY t.XML_MESSAGE.nodes('Contract_Interface') AS bm(XMLData)
--OUTER APPLY bm.XML_MESSAGE.nodes('food') AS f(XMLData)
However, I have created an Schema collection based on the old xsd files for each collection (contract/customer etc) so the real question is how can I use that to extract the relevant data? A cut down version of the xml is below..
<Contract_Interface xmlns="http://company.com/backoffice/types/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://comapny.com/backoffice/types/common http://dataplace/xmlschemas/Policy_Transaction_Interface_1_1_9.xsd">
<Transaction_Id>48244272268</Transaction_Id>
<Source_System_Cd>SystemName</Source_System_Cd>
<Business_Activity>
<Contract_Id>169929</Contract_Id>
<Business_Activity_Type_Cd>01</Business_Activity_Type_Cd>
<Business_Activity_Type_Desc>New Binder</Business_Activity_Type_Desc>
<Out_Of_Sequence_Ind>N</Out_Of_Sequence_Ind>
<Effective_Dt>4/18/2021 12:00:00 AM</Effective_Dt>
<Expiration_Dt>4/18/2022 12:00:00 AM</Expiration_Dt>
<Premium_Amt>123678.00</Premium_Amt>
<Contract>
<Customer>
<Customer_Id>45678</Customer_Id>
<ODS_Customer_Id>6789</ODS_Customer_Id>
<Insured_Info_Overriden_Cd>N</Insured_Info_Overriden_Cd>
<Insured_Info_Overriden_Desc>No</Insured_Info_Overriden_Desc>
<Insured_Nm>Make Money PLC</Insured_Nm>
<Insured_Address>
<Address_Line_1>Alex Jones Terrace</Address_Line_1>
<Address_Line_2 />
<City_Nm>Houston</City_Nm>
<State_Cd>TX</State_Cd>
<State_Desc>Texas</State_Desc>
<Postal_Cd>77002</Postal_Cd>
<Country_Cd>US</Country_Cd>
<Country_Desc>United States</Country_Desc>
</Insured_Address>
</Customer>
<Producer>
<Producer_Id>33333</Producer_Id>
<ODS_Producer_Id>1234</ODS_Producer_Id>
<Producer_No>1234</Producer_No>
<Producer_Nm>Brokerage Limited</Producer_Nm>
<Billing_Address_Overridden_Ind>false</Billing_Address_Overridden_Ind>
<Producer_Billing_Address>
<Address_Line_1>Suite 1000</Address_Line_1>
<Address_Line_2>Free Road</Address_Line_2>
<City_Nm>Hamilton</City_Nm>
<State_Cd />
<State_Desc />
<Postal_Cd>HM11</Postal_Cd>
<Country_Cd>BM</Country_Cd>
<Country_Desc>Bermuda</Country_Desc>
</Producer_Billing_Address>
</Producer>
</Contract>
</Business_Activity>
</Contract_Interface>
As I mentioned, you need to define your Namespace. This uses a variable, instead of your table, but gives you the overlying idea:
DECLARE #XML xml = '<Contract_Interface xmlns="http://company.com/backoffice/types/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://comapny.com/backoffice/types/common http://dataplace/xmlschemas/Policy_Transaction_Interface_1_1_9.xsd">
<Transaction_Id>48244272268</Transaction_Id>
<Source_System_Cd>SystemName</Source_System_Cd>
<Business_Activity>
<Contract_Id>169929</Contract_Id>
<Business_Activity_Type_Cd>01</Business_Activity_Type_Cd>
<Business_Activity_Type_Desc>New Binder</Business_Activity_Type_Desc>
<Out_Of_Sequence_Ind>N</Out_Of_Sequence_Ind>
<Effective_Dt>4/18/2021 12:00:00 AM</Effective_Dt>
<Expiration_Dt>4/18/2022 12:00:00 AM</Expiration_Dt>
<Premium_Amt>123678.00</Premium_Amt>
<Contract>
<Customer>
<Customer_Id>45678</Customer_Id>
<ODS_Customer_Id>6789</ODS_Customer_Id>
<Insured_Info_Overriden_Cd>N</Insured_Info_Overriden_Cd>
<Insured_Info_Overriden_Desc>No</Insured_Info_Overriden_Desc>
<Insured_Nm>Make Money PLC</Insured_Nm>
<Insured_Address>
<Address_Line_1>Alex Jones Terrace</Address_Line_1>
<Address_Line_2 />
<City_Nm>Houston</City_Nm>
<State_Cd>TX</State_Cd>
<State_Desc>Texas</State_Desc>
<Postal_Cd>77002</Postal_Cd>
<Country_Cd>US</Country_Cd>
<Country_Desc>United States</Country_Desc>
</Insured_Address>
</Customer>
<Producer>
<Producer_Id>33333</Producer_Id>
<ODS_Producer_Id>1234</ODS_Producer_Id>
<Producer_No>1234</Producer_No>
<Producer_Nm>Brokerage Limited</Producer_Nm>
<Billing_Address_Overridden_Ind>false</Billing_Address_Overridden_Ind>
<Producer_Billing_Address>
<Address_Line_1>Suite 1000</Address_Line_1>
<Address_Line_2>Free Road</Address_Line_2>
<City_Nm>Hamilton</City_Nm>
<State_Cd />
<State_Desc />
<Postal_Cd>HM11</Postal_Cd>
<Country_Cd>BM</Country_Cd>
<Country_Desc>Bermuda</Country_Desc>
</Producer_Billing_Address>
</Producer>
</Contract>
</Business_Activity>
</Contract_Interface>';
WITH XMLNAMESPACES(DEFAULT 'http://company.com/backoffice/types/common')
SELECT bm.XMLData.value('(Transaction_Id/text())[1]','varchar(100)') AS TransactionID --Should this not be a int or bigint?
FROM #XML.nodes('Contract_Interface') AS bm(XMLData);

Snowflake Get value from XML column

I am working in Snowflake
I need a specific value from XML
SELECT data_xml,REGEXP_SUBSTR(data_xml,'<pinLessNetworkBin>(.*?)(</pinLessNetworkBin>)',3) as network
FROM "DW"."DB"."TABLE"
My results for now
<pinLessNetworkBin>STAR</pinLessNetworkBin>
I just need the value inside
Here the xml:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:FundingSource xmlns:ns0="www.url.com/be/example/payments/model/Concepts/FundingSource" Id="12887819260" extId="">
<id>3939</id>
<pinLessNetworkBin>STAR</pinLessNetworkBin>
</ns0:FundingSource>
How I can get that value?
Regards
the contents of an XML object is retrieved via GET(object, '$') thus for your regex result GET(parse_xml(network), '$') will get you the content. See GET
or you should really retrieve the pinLessNetworkBin via XMLGET:
SELECT data_xml,
XMLGET(parse_xml(data_xml), 'pinLessNetworkBin') as pinLessNetworkBin
FROM "DW"."DB"."TABLE"
parse_xml(data_xml)
which will give you the <pinLessNetworkBin>STAR</pinLessNetworkBin> thus you want to fetch the contents
SELECT data_xml,
get(XMLGET(parse_xml(data_xml), 'pinLessNetworkBin'), '$') as pinLessNetworkBin
FROM "DW"."DB"."TABLE"
parse_xml(data_xml)
should give you 'STAR'
see the PARSE_XML

How to filter data from xml content using xpath queries to create a temporary table

I am trying to create a sql query using xpath where i am looking to filter the data i need and put that in a temporary table.
Example:
<superStarsDoc>
<names>
<starname>
<preferredname>pref</preferredname>
<firstNm>Bradd</firstNm>
<lastNm>Pitt</lastNm>
</starname>
</names>
</superStarsDoc>
and i am trying to get something like this but not working
with data(firstName,lastName) as
(
unnest(xpath('/superStarsDoc/names/starname/firstNm[#firstNm="Bradd"]/text()',
(select xmlparse(document superstar_doc))))::text as firstName
,unnest(xpath('/superStarsDoc/names/starname/lastNm[#lastNm="Pitt"]lastNm="/text()',
(select xmlparse(document superstar_doc))))::text as lastName
from dbname.superstartable
)
I tried searching for solution but i did not find anything specific for my requirement, i dont have any attribute to point to that record exactly.
I tried using the following solution but that is not working, i am getting syntax error.
XPath 1.0 to find if an element's value is in a list of values
Note: I typed the code here as i cannot copy paste my code exactly, so please excuse any typos
You should probably fix your XPath with :
/superStarsDoc/names/starname/firstNm[.="Bradd"]/text()
/superStarsDoc/names/starname/lastNm[.="Pitt"]/text()
Generic code :
with superstartable(superstar_doc) as (
values (
'<?xml version="1.0" encoding="UTF-8"?>
<superStarsDoc>
<names>
<starname>
<preferredname>pref</preferredname>
<firstNm>Bradd</firstNm>
<lastNm>Pitt</lastNm>
</starname>
</names>
</superStarsDoc>
'::xml)
)
SELECT
xpath('/superStarsDoc/names/starname/firstNm[.="Bradd"]/text()', superstar_doc)[1] as "first-name",
xpath('/superStarsDoc/names/starname/lastNm[.="Pitt"]/text()', superstar_doc)[1] as "last-name"
from superstartable

extracting all tags(duplicates also) with specified name from xmltype column in sql

i want to extract a tag from an xml and insert into another table.
this XML is having different name spaces hence i use local-name() to fetch the tag which i want.
but some times there are multiple tags with same name. hence its failing with EXACTFETCH RETURNS MULTIPLE NODES. when multiple tags are existed i want to consider both instead of ignoring the second occurence.
source_table(id, payload):
id : 10
payload :
<root>
<name>john</name>
<status>
<statuscode>1</statuscode>
<statusmessage>one</statusmessage>
<statuscode>2</statuscode>
<statusmessage>two</statusmessage>
</status>
</root>
i want to extract stauscode and message and insert into another table
destination_table(id,name,statuscode,message)
output
10,john,1,one
10,john,2,two
below is the query i used
select id,
extract(payload, '//*[local-name()="name"]'),
extract(payload, '//*[local-name()="statuscode"]'),
extract(payload, '//*[local-name()="statusmessage"]')
from source_table;
i can get first occurence or second occurence by specifying [1] or [2] but i need both the stauscodes to be displayed like below
10,john,1,one
10,john,2,two
any help here
Hope this is what you need: Just past this into an empty query window and execute. Adapt it for your needs:
This solution assumes, that the status codes are sorted (as in your example 1,2,...). If this could occur in random order, just ask again...
Short explanation: The CTE "NameAndCode" brings up the name and the statuscodes. The ROW_NUMBER-function give us the node's index. This index I use to fetch the right message.
One more hint: If you can change the XML's format, it would be better to make the message an attribut of statuscode or to have it as subnode...
DECLARE #xmlColumn XML='<root>
<name>john</name>
<status>
<statuscode>1</statuscode>
<statusmessage>one</statusmessage>
<statuscode>2</statuscode>
<statusmessage>two</statusmessage>
</status>
</root>';
WITH NameAndCode AS
(
SELECT #xmlColumn.value('(/root/name)[1]','varchar(max)') AS Name
,x.y.value('.','int') AS Statuscode
,x.y.query('..') XMLNode
,ROW_NUMBER() OVER(ORDER BY x.y.value('.','int')) AS StatusIndex
FROM #xmlColumn.nodes('/root/status/statuscode') AS x(y)
)
SELECT *
,XMLNode.value('(/status[1]/statusmessage[sql:column("StatusIndex")])[1]','varchar(max)')
FROM NameAndCode