SQL - Extract XML from multiple nodes - sql

I've done a load of research and cannot seem to string together the SQL to extract the required data from an XML field.
<vItem>
<jobScript>
<node guid="7606bd90-98df-4572-accd-5b41ec5605dc">
<subNodes>
<node guid="17f8e275-d4f6-47c0-a5e4-80da658f4097">
<execute taskVersionGuid="5fc17d5c-7264-461f-ae38-753d703f3c99" />
</node>
<node guid="5fe2233c-9e3a-44be-aa20-aea2c8dcbd4a">
<execute taskVersionGuid="f55dc069-46ff-427e-920f-5f1c3fc3ad09" />
</node>
<node guid="ecd6a7b5-a3be-483c-acf8-64ba1c289088">
<execute taskVersionGuid="5220d97c-6e8f-400a-b814-aa7d84942c20" />
</node>
</subNodes>
</node>
</jobScript>
I'm trying to extract the taskVersionGuid from each node. In the scenario, there could be anywhere between 1 and 10 taskVersionGuids, however the example I have above has 3.
Any help with this would be appreciated.
Thanks
Edit
I have tried the below also:
declare #XML xml
set #XML =
'
<vItem>
<jobScript>
<node guid="7606bd90-98df-4572-accd-5b41ec5605dc">
<subNodes>
<node guid="17f8e275-d4f6-47c0-a5e4-80da658f4097">
<execute taskVersionGuid="5fc17d5c-7264-461f-ae38-
753d703f3c99" />
</node>
<node guid="5fe2233c-9e3a-44be-aa20-aea2c8dcbd4a">
<execute taskVersionGuid="f55dc069-46ff-427e-920f-
5f1c3fc3ad09" />
</node>
<node guid="ecd6a7b5-a3be-483c-acf8-64ba1c289088">
<execute taskVersionGuid="5220d97c-6e8f-400a-b814-
aa7d84942c20" />
</node>
</subNodes>
</node>
</jobScript>
</vItem>
'
select T.N.query('.')
from #XML.nodes('/vItem/jobScript/node/subNodes/node/execute') as T(N)
However, this results in the following:
<execute taskVersionGuid="5fc17d5c-7264-461f-ae38-753d703f3c99" />
<execute taskVersionGuid="f55dc069-46ff-427e-920f-5f1c3fc3ad09" />
<execute taskVersionGuid="5220d97c-6e8f-400a-b814-aa7d84942c20" />
Whereas I'm trying to receive the value of taskVersionGuid.
Thanks again.

Answer as below:
select T.N.value('#taskVersionGuid[1]', 'uniqueidentifier')
from #XML.nodes('/vItem/jobScript/node/subNodes/node/execute') as T(N)

What you need to do is turn your xml into a table so you can query it. below is an example of the query you will need to grab the values from the nodes.
DECLARE #xml AS XML = '<jobScript>
<node guid="7606bd90-98df-4572-accd-5b41ec5605dc">
<subNodes>
<node guid="17f8e275-d4f6-47c0-a5e4-80da658f4097">
<execute taskVersionGuid="5fc17d5c-7264-461f-ae38-753d703f3c99" />
</node>
<node guid="5fe2233c-9e3a-44be-aa20-aea2c8dcbd4a">
<execute taskVersionGuid="f55dc069-46ff-427e-920f-5f1c3fc3ad09" />
</node>
<node guid="ecd6a7b5-a3be-483c-acf8-64ba1c289088">
<execute taskVersionGuid="5220d97c-6e8f-400a-b814-aa7d84942c20" />
</node>
</subNodes>
</node>
</jobScript>'
SELECT a.value('.', 'varchar(max)')
FROM #xml.nodes('/jobScript/node/subNodes/node/execute/#taskVersionGuid') a(a)

Related

XSLT_1: Add missing node based on child element value

could you please assist with solution below.
The sources files some times missing hole segment (which has child 'count' element with number 3). In this case I need to map all missing node segment and add some specific values.
Please see correct source xml file below which has all 'node' segments.
<?xml version="1.0" encoding="utf-8"?>
<root>
<group>
<node segment="1">
<count>2</count>
<value>value_2</value>
</node>
<node segment="1">
<count>3</count>
<value>value_3</value>
</node>
<node segment="1">
<count>1</count>
<value>value_1</value>
</node>
</group>
</root>
The below one doesn't have all segments in place.
<?xml version="1.0" encoding="utf-8"?>
<root>
<group>
<node segment="1">
<count>2</count>
<value>value_2</value>
</node>
<node segment="1">
<count>1</count>
<value>value_1</value>
</node>
</group>
</root>
Mising part is:
<node segment="1">
<count>3</count>
<value>value_3</value>
</node>
I have worked on xslt script below which doesn't give me the correct output results. Please assist
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/group[not(node/count='3')]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<node>
<count><xsl:value-of select="'some_results'"></xsl:value-of></count>
<value><xsl:value-of select="'some_results'"/></value>
</node>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node[not(count='3')]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<count><xsl:value-of select="count"></xsl:value-of></count>
<value><xsl:value-of select="value"/></value>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
If element 'node' which has child element 'count'=3 present,- map as it is.
Otherwise the output would be:
<?xml version="1.0" encoding="utf-8"?>
<root>
<group>
<node segment="1">
<count>2</count>
<value>value_2</value>
</node>
<node segment="1">
<count>some_results</count>
<value>some_results</value>
</node>
<node segment="1">
<count>1</count>
<value>value_1</value>
</node>
</group>
</root>
Thanks,
Darius
Couldn't it be simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group[not(node/count='3')]">
<xsl:copy>
<!-- add missing node-->
<node segment="1">
<count>some_results</count>
<value>some_results</value>
</node>
<!-- process existing nodes -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

how to select all parent node in xml data

i want to select in all parent node while i don't know how many nodes exist ?
*<TreeView>
<node text="a">
<node text="aa">
<node text="aaa" />
</node>
<node text="b">
<node text="bb" />
</node>
</node>
<node text="c" />*
</TreeView>
what i want is: a,aa,b
DECLARE #MyXML XML
SET #MyXML = '<TreeView>
<node text="a">
<node text="aa">
<node text="aaa" />
</node>
<node text="b">
<node text="bb" />
</node>
</node>
<node text="c" />*
</TreeView> '
SELECT #MyXML.value ('(//node/#text)[1]', 'VARCHAR(30)'),
#MyXML.value ('(//node/#text)[2]', 'VARCHAR(30)'),
#MyXML.value ('(//node/#text)[4]', 'VARCHAR(30)')

using XSLT to select multiple child nodes

I'm having a bit of trouble selecting exactly what I want in the following input XML.
I need to essentially select by keys/key , where there are multiple <key> child nodes.
For example, here's a sample input XML file :
<Outbound environment="" request="selectPortfolios">
<body matchedRecordCount="4">
<portfolioSummary portfolioId="36">
<portfolio id="36">
<currency>USD</currency>
<keys>
<key displayValue="DummyCounterpartyIRS" sequenceNumber="100" sequenceValue="DummyCounterpartyIRS" type="Counterparty" value="DummyCounterpartyIRS"/>
<key displayValue="Internal VaR IRS LCH" sequenceNumber="2000" sequenceValue="80" type="ExpType" value="InternalVaR_IRS_LCH"/>
</keys>
<status>Unapproved</status>
</portfolio>
<exposureProfile>
<node date="2014-05-06">
<tag>HSVaR 5D 100 ES</tag>
<exposure>492692</exposure>
</node>
</exposureProfile>
</portfolioSummary>
<portfolioSummary portfolioId="37">
<portfolio id="37">
<currency>USD</currency>
<keys>
<key displayValue="DummyCounterpartyIRS" sequenceNumber="100" sequenceValue="DummyCounterpartyIRS" type="Counterparty" value="DummyCounterpartyIRS"/>
<key displayValue="Internal VaR CDS LCH" sequenceNumber="2000" sequenceValue="81" type="ExpType" value="InternalVaR_CDS_LCH"/>
</keys>
<status>Unapproved</status>
</portfolio>
<exposureProfile>
<node date="2014-05-09">
<tag>Node Scenario 4</tag>
<exposure>248057</exposure>
</node>
<node date="2014-05-10">
<tag>Node Scenario 5</tag>
<exposure>373130</exposure>
</node>
<node date="2014-05-11">
<tag>EXPECTED_SHORTFALL 99.7</tag>
<exposure>373130</exposure>
</node>
</exposureProfile>
</portfolioSummary>
<portfolioSummary portfolioId="71">
<readOnly>false</readOnly>
<portfolio id="71">
<currency>USD</currency>
<keys>
<key displayValue="DummyCounterpartyCDS" sequenceNumber="100" sequenceValue="DummyCounterpartyCDS" type="Counterparty" value="DummyCounterpartyCDS"/>
<key displayValue="Internal VaR IRS LCH" sequenceNumber="2000" sequenceValue="80" type="ExpType" value="InternalVaR_IRS_LCH"/>
</keys>
<status>Unapproved</status>
</portfolio>
<exposureProfile>
<node date="2014-05-06">
<tag>HSVaR 5D 100 ES</tag>
<exposure>58</exposure>
</node>
</exposureProfile>
</portfolioSummary>
<portfolioSummary portfolioId="72">
<readOnly>false</readOnly>
<portfolio id="72">
<currency>USD</currency>
<keys>
<key displayValue="DummyCounterpartyCDS" sequenceNumber="100" sequenceValue="DummyCounterpartyCDS" type="Counterparty" value="DummyCounterpartyCDS"/>
<key displayValue="Internal VaR CDS LCH" sequenceNumber="2000" sequenceValue="81" type="ExpType" value="InternalVaR_CDS_LCH"/>
</keys>
<status>Unapproved</status>
</portfolio>
<exposureProfile>
<node date="2014-05-09">
<tag>Node Scenario 4</tag>
<exposure>9</exposure>
</node>
<node date="2014-05-10">
<tag>Node Scenario 5</tag>
<exposure>12</exposure>
</node>
<node date="2014-05-11">
<tag>EXPECTED_SHORTFALL 99.7</tag>
<exposure>12</exposure>
</node>
</exposureProfile>
</portfolioSummary>
</body>
</Outbound>
The specific <portfolioSummary> nodes I want are based on the <key> attributes:
1) First XSLT template:
1a) where attribute type="Counterparty" and value="DummyCounterpartyIRS" AND
1b) where attribute type="ExpType" AND value="InternalVaR_IRS_LCH"
2) Second XSLT template:
2a) where attribute type="Counterparty" value="DummyCounterpartyCDS" AND
2b) where attributes type="ExpType" AND value="InternalVaR_CDS_LCH"
It should end up pulling TWO <portfolioSummary> nodes for both portfolioId="36" and portfolioId="72"
Here's what I have, but it's not selecting properly:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
exclude-result-prefixes="xs xd"
version="1.0">
<!-- Variable declaration -->
<xsl:variable name="hsVar1D" select="'1D (99%)'"></xsl:variable>
<xsl:variable name="hsVar5D" select="'HSVaR 5D 100 ES'"></xsl:variable>
<!-- Pull portfolioSummary nodes for HSVaR -->
<xsl:template match="/*">
<collection>
<xsl:apply-templates select="/Outbound/body/portfolioSummary[descendant::portfolio/keys/key[#type='Counterparty' and #value='DummyCounterpartyIRS']]"/>
<xsl:apply-templates select="/Outbound/body/portfolioSummary[descendant::portfolio/keys/key[#type='Counterparty' and #value='DummyCounterpartyCDS']]"/>
</collection>
</xsl:template>
<xsl:template match="*">
<xsl:choose>
<xsl:when test="descendant::portfolio/keys/key[#type='ExpType' and #value='InternalVaR_IRS_LCH']">
<extIA>
<AGREEMENTID><xsl:value-of select="#portfolioId"></xsl:value-of></AGREEMENTID>
<legal_id><xsl:value-of select="portfolio/keys/key[#type='Counterparty']/#displayValue"/></legal_id>
<PRODUCT><xsl:value-of select="portfolio/keys/key[#type='Counterparty']/#displayValue"/></PRODUCT>
<AMOUNT><xsl:value-of select="exposureProfile/node[tag/text()[contains(.,$hsVar5D)]]/exposure"/></AMOUNT>
<CURRENCY><xsl:value-of select="bandStructure/currency"/></CURRENCY>
<ValuationDate>2012-05-15</ValuationDate>
<externalSystem>MY EXT SYSTEM</externalSystem>
</extIA>
</xsl:when>
<xsl:when test="descendant::portfolio/keys/key[#type='ExpType' and #value='InternalVaR_CDS_LCH']">
<extIA>
<AGREEMENTID><xsl:value-of select="#portfolioId"></xsl:value-of></AGREEMENTID>
<legal_id><xsl:value-of select="portfolio/keys/key[#type='Counterparty']/#displayValue"/></legal_id>
<PRODUCT><xsl:value-of select="portfolio/keys/key[#type='ExpType']/#displayValue"/></PRODUCT>
<AMOUNT><xsl:value-of select="exposureProfile/node[tag/text()[contains(.,$hsVar5D)]]/exposure"/></AMOUNT>
<CURRENCY><xsl:value-of select="bandStructure/currency"/></CURRENCY>
<ValuationDate>2012-05-15</ValuationDate>
<externalSystem>MY EXT SYSTEM</externalSystem>
</extIA>
</xsl:when>
<xsl:otherwise></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The <xsl:apply-templates> in your first template are not selecting anything. You are matching /* and selecting an absolute location path which starts with an element that does not exist: razorOutbound.
Change it to:
<xsl:template match="/">
<collection>
<xsl:apply-templates select="Outbound/body/portfolioSummary[descendant::portfolio/keys/key[#type='Counterparty' and #value='DummyCounterpartyIRS']]"/>
<xsl:apply-templates select="Outbound/body/portfolioSummary[descendant::portfolio/keys/key[#type='Counterparty' and #value='DummyCounterpartyCDS']]"/>
</collection>
</xsl:template>
As for the selections, four portfolios are selected, and not two. The first expression:
//body/portfolioSummary[portfolio/keys/key[#type='Counterparty' and #value='DummyCounterpartyIRS']]
will match two portfolios (36 and 37), and the other will match 71 and 72. The * template will be called twice for each pair and will always match one or the other xsl:when. If you want to select only one of each, you have to add some other restriction to it.

Dynamic parameters for XSLT 2.0 group-by

I got this input
<?xml version="1.0" encoding="UTF-8"?>
<result>
<datapoint poiid="2492" period="2004" value="1240"/>
<datapoint poiid="2492" period="2005" value="1290"/>
<datapoint poiid="2492" period="2006" value="1280"/>
<datapoint poiid="2492" period="2007" value="1320"/>
<datapoint poiid="2492" period="2008" value="1330"/>
<datapoint poiid="2492" period="2009" value="1340"/>
<datapoint poiid="2492" period="2010" value="1340"/>
<datapoint poiid="2492" period="2011" value="1335"/>
<datapoint poiid="2493" period="2004" value="1120"/>
<datapoint poiid="2493" period="2005" value="1120"/>
<datapoint poiid="2493" period="2006" value="1100"/>
<datapoint poiid="2493" period="2007" value="1100"/>
<datapoint poiid="2493" period="2008" value="1100"/>
<datapoint poiid="2493" period="2009" value="1110"/>
<datapoint poiid="2493" period="2010" value="1105"/>
<datapoint poiid="2493" period="2011" value="1105"/>
</result>
and I use this xslt 2.0
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="result">
<xsl:for-each-group select="datapoint" group-by="#poiid">
<node type="poiid" id="{#poiid}">
<xsl:for-each select="current-group()">
<node type="period" id="{#period}" value="{#value}"/>
</xsl:for-each>
</node>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
to convert it into
<?xml version="1.0" encoding="UTF-8"?>
<node type="poiid" id="2492">
<node type="period" id="2004" value="1240"/>
<node type="period" id="2005" value="1290"/>
<node type="period" id="2006" value="1280"/>
<node type="period" id="2007" value="1320"/>
<node type="period" id="2008" value="1330"/>
<node type="period" id="2009" value="1340"/>
<node type="period" id="2010" value="1340"/>
<node type="period" id="2011" value="1335"/>
</node>
<node type="poiid" id="2493">
<node type="period" id="2004" value="1120"/>
<node type="period" id="2005" value="1120"/>
<node type="period" id="2006" value="1100"/>
<node type="period" id="2007" value="1100"/>
<node type="period" id="2008" value="1100"/>
<node type="period" id="2009" value="1110"/>
<node type="period" id="2010" value="1105"/>
<node type="period" id="2011" value="1105"/>
</node>
Works smoothly.
Where I got stuck is when I tried to make it more dynamic. The real life input has 6 attributes for each datapoint instead of 3, and the usecase requires the possibility to set the grouping parameters dynamically.
I tried using parameters
<xsl:param name="k1" select="'poiid'"/>
<xsl:param name="k2" select="'period'"/>
but passing them to the rest of the xslt is something that I can't get right. The code below doesn't work, but clarifies hopefully, what I'm looking for.
<xsl:template match="result">
<xsl:for-each-group select="datapoint" group-by="#{$k1}">
<node type="{$k1}" id="#{$k1}">
<xsl:for-each select="current-group()">
<node type="{$k2}" id="#{$k2}" value="{#value}"/>
</xsl:for-each>
</node>
</xsl:for-each-group>
</xsl:template>
Any help appreciated..
Here is how to do it:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="k1" select="'poiid'"/>
<xsl:param name="k2" select="'period'"/>
<xsl:template match="result">
<xsl:for-each-group select="datapoint" group-by="#*[name()= $k1]">
<node type="{$k1}" id="{#*[name() = $k1]}">
<xsl:for-each select="current-group()">
<node type="{k2}" id="{#*[name()= $k2]}" value="{#value}"/>
</xsl:for-each>
</node>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<result>
<datapoint poiid="2492" period="2004" value="1240"/>
<datapoint poiid="2492" period="2005" value="1290"/>
<datapoint poiid="2492" period="2006" value="1280"/>
<datapoint poiid="2492" period="2007" value="1320"/>
<datapoint poiid="2492" period="2008" value="1330"/>
<datapoint poiid="2492" period="2009" value="1340"/>
<datapoint poiid="2492" period="2010" value="1340"/>
<datapoint poiid="2492" period="2011" value="1335"/>
<datapoint poiid="2493" period="2004" value="1120"/>
<datapoint poiid="2493" period="2005" value="1120"/>
<datapoint poiid="2493" period="2006" value="1100"/>
<datapoint poiid="2493" period="2007" value="1100"/>
<datapoint poiid="2493" period="2008" value="1100"/>
<datapoint poiid="2493" period="2009" value="1110"/>
<datapoint poiid="2493" period="2010" value="1105"/>
<datapoint poiid="2493" period="2011" value="1105"/>
</result>
the wanted, correct result is produced:
<node type="poiid" id="2492">
<node type="" id="2004" value="1240"/>
<node type="" id="2005" value="1290"/>
<node type="" id="2006" value="1280"/>
<node type="" id="2007" value="1320"/>
<node type="" id="2008" value="1330"/>
<node type="" id="2009" value="1340"/>
<node type="" id="2010" value="1340"/>
<node type="" id="2011" value="1335"/>
</node>
<node type="poiid" id="2493">
<node type="" id="2004" value="1120"/>
<node type="" id="2005" value="1120"/>
<node type="" id="2006" value="1100"/>
<node type="" id="2007" value="1100"/>
<node type="" id="2008" value="1100"/>
<node type="" id="2009" value="1110"/>
<node type="" id="2010" value="1105"/>
<node type="" id="2011" value="1105"/>
</node>

Use SQL Server 2005 XML APIs to normalize an XML fragment

I have some (untyped) XML being stored in SQL Server 2005 that I need to transform into a normalized structure. The structure of the document currently looks like so:
<wrapper>
<parent />
<node />
<node />
<node />
<parent />
<node />
<node />
<node />
<wrapper>
I want to transform it to look like this:
<wrapper>
<parent>
<node />
<node />
<node />
</parent>
<parent>
<node />
<node />
<node />
</parent>
<wrapper>
I can select the XML out into a relational structure if I need to, put the problem is there are no attributes linking the parent and the child nodes together, so order becomes an issue when using set-based operations. How can I use the .nodes()/.value()/other SQL Server XML APIs to transform this data? The transformation needs to run as part of a batch SQL script so extracting it into another tool/language is not a reasonable option for me.
Actually - following code works (grouping here may be isn't very optimal, but anyway):
declare #xml xml = '
<wrapper>
<parent id="1" />
<node id="1" />
<node id="2" />
<node id="3" />
<parent id="2" />
<node id="4" />
<node id="5" />
<node id="6" />
</wrapper>
'
;with px as
(
select row_number() over (order by (select 1)) as RowNumber
,t.v.value('#id', 'int') as Id
,t.v.value('local-name(.)', 'nvarchar(max)') as TagName
from #xml.nodes('//wrapper/*') as t(v)
)
select p.Id as [#id],
(
select n.Id as id
from px n
where n.TagName = 'node'
and n.RowNumber > p.RowNumber
and not exists
(
select null
from px np
where np.TagName = 'parent'
and np.RowNumber > p.RowNumber
and np.RowNumber < n.RowNumber
)
order by n.RowNumber
for xml raw('node'), type
)
from px p
where p.TagName = 'parent'
order by p.RowNumber
for xml path('parent'), root('wrapper')
But I don't recommend to use it. See here: http://msdn.microsoft.com/en-us/library/ms172038%28v=sql.90%29.aspx: In SQLXML 4.0, document order is not always determined
So I'm not sure that we can rely on order of tags inside wrapper (and code above is more just for fun than for practical use).