extracting all tags(duplicates also) with specified name from xmltype column in sql - 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

Related

Fetch a value from a column which contains XML

How to fetch a value from a column which contains XML in SQL Server?
below is my sample XML column value and the id's can be swap anytime(101,100) or (201,100,101,321).
<Questions>
<item id="101">Yes</item>
<item id="100">No</item>
</Questions>
I want to fetch a value based on Id. Like fetching Yes from id=101.
Sample code much appreciated.
I tried with below sample, but Unable to retrieve value "Yes"
select Y.value('#item[1]','varchar[3]') as valT from tbl_storeXML s cross apply s.Questions.nodes('Questions/item') as X(Y) where e.empId=256 and Y.value('#id','int')=101
Please help on this.
Ps. It's not a home work, I am learning handling xml in sql server.
Use of the value is not done correct, you do:
Y.value('#id','int')
This should be: Y.value('(#id)[1]','int')
round braces around #id, see: docs: value() Method
and Y.value('item[1]','varchar[3]').
This should be: Y.value('(#item)[1]','varchar(3)').
The # is removed because item is not an attribute
varchar should have round braces, not square braces.
Your try, after changes will become:
select
Y.value('(item)[1]','varchar(3)') as valT
from tbl_storeXML s
cross apply s.Questions.nodes('Questions/item') as X(Y)
where e.empId=256 and Y.value('(#id)','int')=101
This is not tested, because I do not have those tables. (I do think Y.value('(item)[1]','varchar(3)') might need to be written as Y.value('(.)[1]','varchar(3)') )
But the same approach can be seen in this DBFIDDLE
DECLARE #xml XML = '<Questions>
<item id="101">Yes</item>
<item id="100">No</item>
</Questions>';
select
X.y.value('(#id)[1]','VARCHAR(20)') id,
X.y.value('(.)[1]','VARCHAR(20)') value
from #xml.nodes('Questions/item') as X(y);
output:
id
value
101
Yes
100
No

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.

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

SQL query to get most recent date from XML document

I am doing a SQL query against a column with an XML document located.
The XML document looks like the following.
<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>
As you can see the dates are out of order.
I am looking for a SQL query that will get the most recent date from the XML document (in this case: 2016-12-01).
One way is to read all data and find the maximum externally (external ORDER BY with TOP 1, like in Prdp's answer, or MAX(), eventually with GROUP BY).
Another way is a FLWOR-XQuery:
DECLARE #xml XML=
'<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>';
SELECT #xml.value('max(for $d in /root/date return xs:date($d))','date')
This means:
Take each value in /root/date, return it as date and find the highest!
Both approaches will need to read the whole list, but it should be a bit faster only to look for the maximum value, rather than return a full list and do some external sorting, picking again...
Try this
DECLARE #xml XML
SET #xml = '<root>
<date>2016-10-12</date>
<date>2016-12-01</date>
<date>2016-11-13</date>
</root>'
SELECT Top 1 x.col.value('.', 'date') AS dates
FROM #xml.nodes('/root/date') x(col)
ORDER BY dates DESC

Parse XML in T-SQL

I know that there are already numerous topics about querying xml in MS T-SQL, however with all the samples, I wasn't able to get my query to work properly.
I have the following XML:
<group>
<items>
<groupitem>
<key>23137</key>
</groupitem>
<groupitem>
<key>23139</key>
</groupitem>
<groupitem>
<key>23151</key>
</groupitem>
<groupitem>
<key>23153</key>
</groupitem>
</items>
</group>
I want to get all the 'key' items, so that I can insert them into a table (so 4 rows)
I started off putting my xml into variable #xml and running this query:
SELECT doc.value('(key/text())[1]', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
That gave me 4 empty rows, and if I remove the [1], it gives me this error: "XQuery [value()]: '*value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic '"
Then I tried this:
SELECT doc.value('(/group/items/groupitem/key)[1]', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
That actually gave me some data, but unfortunately, it's 4 times the same key 23137, probably caused by the [1] in the statement. Removing it however brings me back to the same error message as before.
I know how I should do it in Xpath (/group/items/groupitem/key), but can't get my head around how I should do it in T-Sql. Any ideas?
In the following query, context element of doc is already key element :
SELECT doc.value('(key/text())[1]', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
So you should not mention key again in the select clause. You can use . instead to reference current context element :
SELECT doc.value('.', 'nvarchar(255)') AS 'key'
FROM #xml.nodes('/group/items/groupitem/*') AS ref(doc)
Try this:
SELECT
doc.value('(key)[1]', 'int') AS 'key'
FROM
#xml.nodes('/group/items/groupitem') AS ref(doc)
In my case, this returns an output like:
key
-----
23137
23139
23151
23153
Is that what you're looking for?
The call to .nodes() basically gives you a "pseudo" table of XML fragments - one for each match of the XPath expression. So in your case, you get four rows of XML back, each representing the contents of the <groupitem> node. You reach into that, grab the value of the <key> element contained inside, cast it to an int - and presto, you're done!