Parse an xml file without dups - sql-server

How can I have the Attribute_ID in the column and Attribute_Value as value to be populated in the table.
Below is XML doc:
<ExperianBureauData>
<NetConnectResponse>
<CompletionCode>0000</CompletionCode>
<Products>
<CreditProfile>
<ARF_Report>
<n836_-_Premier_Attributes>
<Record_ID>836</Record_ID>
<Record_Length>314</Record_Length>
<Message_Code>1</Message_Code>
<Attribute>
<Attribute_ID>ALL0135</Attribute_ID>
<Attribute_Value>000000002</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL2306</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL2336</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL5742</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL5935</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL8220</Attribute_ID>
<Attribute_Value>000000044</Attribute_Value>
</Attribute>
</n836_-_Premier_Attributes>
</ARF_Report>
</CreditProfile>
</Products>
</NetConnectResponse>
</ExperianBureauData>
I tried:
SELECT DISTINCT
-- -- -- -- <<n836_-_Premier_Attributes>/<Attribute>/
-- -- -- -- Tbl7
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL0135
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL2306
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL2336
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL5742
FROM [dbo].[STG_XML_TstTbl]
CROSS APPLY [XMLReport].nodes('/ExperianBureauData/NetConnectResponse/Products/CreditProfile/ARF_Report/n836_-_Premier_Attributes/Attribute') AS Tbl5(Tbl5);

Consider the following query based on that in your question...
declare #xml xml = N' <ExperianBureauData>
<NetConnectResponse>
<CompletionCode>0000</CompletionCode>
<Products>
<CreditProfile>
<ARF_Report>
<n836_-_Premier_Attributes>
<Record_ID>836</Record_ID>
<Record_Length>314</Record_Length>
<Message_Code>1</Message_Code>
<Attribute>
<Attribute_ID>ALL0135</Attribute_ID>
<Attribute_Value>000000002</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL2306</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL2336</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL5742</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL5935</Attribute_ID>
<Attribute_Value>000000000</Attribute_Value>
</Attribute>
<Attribute>
<Attribute_ID>ALL8220</Attribute_ID>
<Attribute_Value>000000044</Attribute_Value>
</Attribute>
</n836_-_Premier_Attributes>
</ARF_Report>
</CreditProfile>
</Products>
</NetConnectResponse>
</ExperianBureauData>';
SELECT DISTINCT
Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL0135
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL2306
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL2336
,Tbl5.value('Attribute_Value[1]','VARCHAR(20)') AS ALL5742
FROM #xml.nodes('/ExperianBureauData/NetConnectResponse/Products/CreditProfile/ARF_Report/n836_-_Premier_Attributes/Attribute') AS Tbl5(Tbl5);
Each column is querying the same Attribute_Value element, Tbl5.value('Attribute_Value[1]','VARCHAR(20)'), so this returns the following results:
ALL0135
ALL2306
ALL2336
ALL5742
000000000
000000000
000000000
000000000
000000002
000000002
000000002
000000002
000000044
000000044
000000044
000000044
To return values matching the output column names you need to restructure the XPath query to be like the following...
SELECT
Tbl5.value('(Attribute[Attribute_ID="ALL0135"]/Attribute_Value)[1]','VARCHAR(20)') AS ALL0135,
Tbl5.value('(Attribute[Attribute_ID="ALL2306"]/Attribute_Value)[1]','VARCHAR(20)') AS ALL2306,
Tbl5.value('(Attribute[Attribute_ID="ALL2336"]/Attribute_Value)[1]','VARCHAR(20)') AS ALL2336,
Tbl5.value('(Attribute[Attribute_ID="ALL5742"]/Attribute_Value)[1]','VARCHAR(20)') AS ALL5742,
Tbl5.value('(Attribute[Attribute_ID="ALL5935"]/Attribute_Value)[1]','VARCHAR(20)') AS ALL5935,
Tbl5.value('(Attribute[Attribute_ID="ALL8220"]/Attribute_Value)[1]','VARCHAR(20)') AS ALL8220
FROM #xml.nodes('/ExperianBureauData/NetConnectResponse/Products/CreditProfile/ARF_Report/n836_-_Premier_Attributes') AS Tbl5(Tbl5);
Which returns the results:
ALL0135
ALL2306
ALL2336
ALL5742
ALL5935
ALL8220
000000002
000000000
000000000
000000000
000000000
000000044

Related

My XML content contains &#xd; why is XSLT 1.0 not converting this into line breaks?

I have multi line text present in an SQL column, and I am fetching the data from an SQL query. My query is inserting at the end of each line.
Query:
Declare #vcFooterText Varchar(max)
Select #vcFooterText = (Select replace(replace(AppConfigValue,CHAR(13),'
'),CHAR(10),'
') From COM.Config_Application With (NoLock) Where AppConfigId = 'PackingNoteFooterText')
Select #vcFooterText
Query Result:
Line 1
Line 2 
Line 3
Then XML is used which is showing the data:
Select tbl.OrderNumber, Cast(
(
'<Documents><Document><PackingNote>' +
Cast((
Select
CustomerName CustomerName,
Convert(Varchar(10), OrderCreatedOn, 103) + ' ' + Convert(Varchar(5), OrderCreatedOn, 114) As OrderDate,
CustomerNumber CustomerNumber,
OrderNumber OrderNumber,
BatchNumber BatchNumber,
ChannelOrderNumber ChannelOrderNumber,
OrderChannel OrderChannel,
Case
When DM.Category = 1501 ANd DM.SubCategory <> 1604 Then 'Click and Collect'
When DM.Category = 1502 Then 'Home Delivery'
End 'OrderType',
#vCompanyName as CompanyName,
#vCompanyEmail as CompanyEmail,
#vCompanyURL as CompanyURL,
#vCollectionPointValue as CollectionPointValue,
Case
when #vCheckCollectionPoint = 'True' Then 'Collection Point'
END 'CollectionPoint',
Case
When DM.Category = 1501 ANd DM.SubCategory <> 1604 Then 'Collect'
When DM.Category = 1502 Then 'Despatch'
When DM.Category = 1501 And DM.SubCategory = 1604 Then (Select AppConfigValue from Catalogue.COM.Config_Application
Where AppConfigId='SameDayShippingLabel')
End 'DeliveryMethod',
(Case When DM.Category = 1502 Then 1 Else 0 End) As 'IsHomeDelivery',
(CASE
WHEN DM.Category = 1502
THEN
CAST(
(
SELECT
ISNULL(OA.Name, CustomerName) CustomerName,
OA.AddressLines AddressLines,
OA.City City,
OA.PostalCode PostalCode,
OA.Country Country
FROM COM.Order_Address OA WITH (NoLock)
INNER JOIN COM.DeliveryMethod DM WITH (NoLock) ON DM.pkDeliveryMethodId = tbl.DeliveryMethodId
AND (DM.Category = 1502
OR DM.SubCategory = 1604) --HomeDelivery
AND OA.OrderId = tbl.OrderId
AND OA.AddressType = 'SHIP'
FOR
XML PATH('')
) AS XML
)
ELSE
CAST(
(
SELECT
ISNULL(B.Name, CustomerName) CustomerName,
A.Street AddressLines,
A.City City,
A.PostalCode PostalCode,
A.County Country
FROM Catalogue..Branch B WITH (NoLock)
INNER JOIN Catalogue.COM.[Order] O WITH (NoLock) ON O.CollectStoreId = B.pkBranchID
INNER JOIN Catalogue..[Address] A WITH (NoLock) ON A.fkBranchID = O.CollectStoreId
INNER JOIN COM.DeliveryMethod DM WITH (NoLock) ON DM.pkDeliveryMethodId = tbl.DeliveryMethodId
AND (DM.Category = 1501
OR DM.SubCategory <> 1604) --CC
AND O.pkOrderId = tbl.OrderId
FOR
XML PATH('')
) AS XML
)
END )
AS 'DeliveryAddress'
From COM.DeliveryMethod DM With (NoLock) Where DM.pkDeliveryMethodId = tbl.DeliveryMethodId
For Xml Path('Header'), Type
) As NVarchar(Max)) +
Cast((
Select SKU As 'Line/SKU',
Quantity As 'Line/Quantity',
ProductName As 'Line/ProductName',
Barcode As 'Line/Barcode',
Price As 'Line/Price'
From #tblPrintOrderLines Where OrderNumber = tbl.OrderNumber
For Xml Path('Lines'), Type
) As Varchar(Max)) +
Cast((
Select SUM(Quantity),
OrderNotes OrderNotes,
#vcFooterText As Text
From #tblPrintOrderLines Where OrderNumber = tbl.OrderNumber
For Xml Path('TotalItems'), Type
) As Varchar(Max)) +
'</PackingNote></Document></Documents>'
)
As XML) As 'XML',
#xmlTemplate As XSLT
From #tblOrderData tbl
XML content:
This is the XML content that is produced:
<Documents>
<Document>
<PackingNote>
<Header>
<CustomerName>laura haines</CustomerName>
<OrderDate>15/09/2017 20:38</OrderDate>
<CustomerNumber>000003</CustomerNumber>
<OrderNumber>000024</OrderNumber>
<BatchNumber>1 of 1</BatchNumber>
<OrderType>Click and Collect</OrderType>
<CompanyName>The Retail Suite</CompanyName>
<CompanyURL />
<DeliveryMethod>Collect</DeliveryMethod>
<IsHomeDelivery>0</IsHomeDelivery>
<DeliveryAddress>
<CustomerName>ACC</CustomerName>
<AddressLines>London</AddressLines>
<City>London</City>
</DeliveryAddress>
</Header>
<Lines>
<Line>
<SKU>000044</SKU>
<Quantity>9</Quantity>
<ProductName>Partial Cancel 001</ProductName>
<Price>135.00</Price>
</Line>
</Lines>
<TotalItems>9<Text>Line 1&#xD;Line 2 &#xD;Line 3</Text></TotalItems>
</PackingNote>
</Document>
</Documents>
How I can get line breaks using XSL 1.0?
XSLT 1.0:
<fo:table-row>
<fo:table-cell padding-bottom="1mm" padding-left="0.5cm">
<fo:block font-size="10pt" text-align="left">
<xsl:value-of select="text" />
</fo:block>
</fo:table-cell>
</fo:table-row>
Current output:
Line 1
Line 2 
Line 3
Expected Output:
Line 1
Line 2
Line 3
If you're using XSLT 1.0, replace this line:
<xsl:value-of select="text" />
with:
<xsl:call-template name="replace">
<xsl:with-param name="text" select="text"/>
</xsl:call-template>
and add this to your stylesheet:
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="search-string" select="'&#xD;'"/>
<xsl:param name="replace-string" select="'
'"/>
<xsl:choose>
<xsl:when test="contains($text, $search-string)">
<xsl:value-of select="substring-before($text, $search-string)"/>
<xsl:value-of select="$replace-string"/>
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text, $search-string)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Note: XML is case-sensitive, and in your XML the element is named Text not text. So it should really be:
<xsl:call-template name="replace">
<xsl:with-param name="text" select="Text"/>
</xsl:call-template>
How I can get line breaks using XSL 1.0?
In your query, you can replace the CHAR with
OR
instead of
OR
In XSLT 1.0, you can do the same as below:
<fo:table-row>
<fo:table-cell padding-bottom="1mm" padding-left="0.5cm">
<fo:block font-size="10pt" text-align="left">
<xsl:value-of select="translate(text,'&#xd;','
')" />
</fo:block>
</fo:table-cell>
</fo:table-row>
XSLT would convert
OR
in line breaks. Refer to the updated transformation here.
To correct:
In XSLT 1.0, it's needed to write a recursive template to replace a string or xmlns:str="xalan://org.apache.commons.lang.StringUtils" can be used, which provides the `replace()' method.

How to fetch all data from XML column in SQL Server

I am trying to fetch the data from a XML column. This is the query and below it is the xml column data
SELECT
[PQDAdvice].value('(/Advice//DORowData/PrimaryValue/node())[1]', 'nvarchar(max)') as PrimaryValue,
[PQDAdvice].value('(/Advice//DORowData/ListValue1/node())[1]', 'nvarchar(max)') as ListValue1
FROM
PatQD
WHERE
PQDPatID = '4c983bd8-da00-4395-80bb-a383b21313d5'
XML contents:
<Advice>
<DORowData>
<PrimaryValue>Diet and Nutrition</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
<DORowData>
<PrimaryValue>Salt Restriction</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
<DORowData>
<PrimaryValue>Water Consumption</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
</Advice>
The issue I am getting that only one column I am getting instead of all column
Primary |ListValue1
Diet and Nutrition |Advice
Instead of
Primary |ListValue1
Diet and Nutrition |Advice
Salt Restriction |Advice
Water Consumption |Advice
DECLARE #xml XML = '
<Advice>
<DORowData>
<PrimaryValue>Diet and Nutrition</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
<DORowData>
<PrimaryValue>Salt Restriction</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
<DORowData>
<PrimaryValue>Water Consumption</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
</Advice>'
SELECT
t.c.value('PrimaryValue[1]', 'nvarchar(max)') as PrimaryValue,
t.c.value('ListValue1[1]', 'nvarchar(max)') as ListValue1
FROM #xml.nodes('Advice/DORowData') t(c)
Output -
PrimaryValue ListValue1
----------------------- ------------
Diet and Nutrition Advice
Salt Restriction Advice
Water Consumption Advice
Your query -
SELECT
PrimaryValue = t.c.value('PrimaryValue[1]', 'NVARCHAR(1000)'),
ListValue1 = t.c.value('ListValue1[1]', 'NVARCHAR(1000)')
FROM PatQD
CROSS APPLY PQDAdvice.nodes('Advice/DORowData') t(c)
where PQDPatID = '4c983bd8-da00-4395-80bb-a383b21313d5'
Try it like this
DECLARE #x XML=
'<Advice>
<DORowData>
<PrimaryValue>Diet and Nutrition</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
<DORowData>
<PrimaryValue>Salt Restriction</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
<DORowData>
<PrimaryValue>Water Consumption</PrimaryValue>
<ListValue1>Advice</ListValue1>
</DORowData>
</Advice>';
SELECT DoRowData.value('PrimaryValue[1]','varchar(max)') AS PrimaryValue
,DoRowData.value('ListValue1[1]','varchar(max)') AS ListValue1
FROM #x.nodes('/Advice/DORowData') AS One(DORowData)

mssql parse nested xml

I have an xml message that I need to get the test information out of and into a table using a stored procedure.
I've been using this query:
select distinct
'N' as ORIGSTS,
doc1.Samples.value('(ID)[1]', 'nvarchar(20)') as 'SAMPLE_ID',
doc2.Tests.value('(Name)[1]', 'nvarchar(20)') as 'TEST_NAME'
from
#messageXml.nodes('/CDFAOrderMsg/Samples/Sample') as doc1(Samples),
#messageXml.nodes('/CDFAOrderMsg/Samples/Sample/Tests/Test') as doc2(Tests)
where doc1.Samples.value('(ID)[1]', 'nvarchar(20)') = 456
order by 2, 3
The problem is that it returns the sample ID 456 along with all tests listed in the message. I need to be able to extract the test names along with their associated sample Id to insert into a table. Currently, with two samples and three tests each it returns 12 rows when it should only return 6.
How can I make it return a list of all samples along with their respective test names?
Thanks,
Scott
<OrderMsg xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Samples>
<SourceType>Non-Animal</SourceType>
<Sample>
<ID>456</ID>
<Tests>
<Test>
<Name>SPC</Name>
</Test>
<Test>
<Name>COL</Name>
</Test>
<Test>
<Name>ANTI</Name>
</Test>
</Tests>
</Sample>
<Sample>
<ID>457</ID>
<Tests>
<Test>
<Name>HPC</Name>
</Test>
<Test>
<Name>DEL</Name>
</Test>
<Test>
<Name>NVT</Name>
</Test>
</Tests>
</Sample>
</Samples>
</OrderMsg>
Here is a query that gives the expected result using a outer apply function to get the child nodes collection.
DECLARE #x xml
SET #x = '<OrderMsg xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Samples>
<SourceType>Non-Animal</SourceType>
<Sample>
<ID>456</ID>
<Tests>
<Test>
<Name>SPC</Name>
</Test>
<Test>
<Name>COL</Name>
</Test>
<Test>
<Name>ANTI</Name>
</Test>
</Tests>
</Sample>
<Sample>
<ID>457</ID>
<Tests>
<Test>
<Name>HPC</Name>
</Test>
<Test>
<Name>DEL</Name>
</Test>
<Test>
<Name>NVT</Name>
</Test>
</Tests>
</Sample>
</Samples>
</OrderMsg>'
SELECT DISTINCT
'N' AS ORIGSTS,
s.sampleNode.query('ID').value('.', 'nvarchar(20)') AS 'SAMPLE_ID',
t.testNode.query('Test/Name').value('.', 'nvarchar(20)') AS 'TEST_NAME'
FROM #x.nodes('//Samples/Sample') s (sampleNode)
OUTER APPLY (SELECT
x.testNode.query('.') testNode
FROM sampleNode.nodes('Tests/Test') x (testNode)) t
WHERE s.sampleNode.value('(ID)[1]', 'nvarchar(20)') = 456
ORDER BY 2, 3

XQuery parent and child

Example:
DECLARE #XML XML = '
<Items>
<document id="doc1" value="100">
<details>
<detail detailID="1" detailValue="20"/>
<detail detailID="2" detailValue="80"/>
</details>
</document>
<document id="doc2" value="0">
<details>
</details>
</document>
</Items>
'
I want results like this:
id value detailID detailValue
doc1 100 1 20
doc1 100 2 80
doc2 0 NULL NULL
Tried:
SELECT document.value('../../#docID', 'VARCHAR(10)') AS 'docID',
document.value('../../#value', 'INT') AS 'value',
document.value('#detailID', 'VARCHAR(10)') AS 'detailID',
document.value('#detailValue', 'INT') AS 'detailValue'
FROM #XML.nodes('Items/document/details/detail') AS Documents(document)
But, doc2 is not listed... Also, tried with CROSS JOIN and INNER JOIN, but performance is very bad.
Try this:
SELECT document.value('#id', 'VARCHAR(10)') AS docID,
document.value('#value', 'INT') AS value,
Detail.value('#detailID', 'INT') as DetailId,
Detail.value('#detailValue', 'INT') as DetailValue
FROM #XML.nodes('Items/document') AS Documents(document)
outer apply Documents.document.nodes('details/detail') as Details(Detail);
Just one added detail:
#XML.nodes('//whatever_depth') AS Documents(document)
Using '//' Allows you to query not directly from root
Regards,
Dennes

XQuery to combine node values with Group By logic

I’m looking for an XQuery that will take:
<root>
<entity>
<entityid>1</entityid>
<sometext>this is some text</sometext>
</entity>
<entity>
<entityid>1</entityid>
<sometext>this is some more text</sometext>
</entity>
</root>
And produce a recordset like:
Entityid sometext
1 this is some textthis is some more text
Essentially, combining the values in the 'sometext' nodes while grouping by the entityid. I figured I might be able to accomplish this with loops, but wasn't sure if there was a better way, possibly with a join/group by
declare #XML xml =
'<root>
<entity>
<entityid>1</entityid>
<sometext>this is some text</sometext>
</entity>
<entity>
<entityid>1</entityid>
<sometext>this is some more text</sometext>
</entity>
<entity>
<entityid>2</entityid>
<sometext>Another entity</sometext>
</entity>
</root>';
select T.entityid,
#XML.query('/root/entity[entityid = sql:column("T.entityid")]/sometext').value('.', 'nvarchar(max)') as sometext
from (
select distinct T.N.value('entityid[1]', 'int') as entityid
from #XML.nodes('/root/entity') as T(N)
) as T;
Result:
entityid sometext
----------- -----------------------------------------
1 this is some textthis is some more text
2 Another entity
You could also do that using a more XQuery-based solution, eg
DECLARE #xml XML = '<root>
<entity>
<entityid>1</entityid>
<sometext>this is some text</sometext>
</entity>
<entity>
<entityid>1</entityid>
<sometext>this is some more text</sometext>
</entity>
<entity>
<entityid>2</entityid>
<sometext>Another entity</sometext>
</entity>
</root>'
select
x.c.value('#entityId', 'int') entityId,
x.c.value('.', 'varchar(max)') someText
from
(
select #xml.query('for $e in distinct-values(root/entity/entityid)
return <m entityId = "{$e}">{data(root/entity[entityid = $e]/sometext)}</m>')
) r(c)
cross apply r.c.nodes('m') x(c)
Thanks to Mikael for xml / extra scenario.

Resources