Concat xml node values in MS SQL - sql

I need to get the childnode values of users with multiple keys, as shown below:
<Individuals>
<User key="0">
<UserlID>100</UserlID>
<FirstName>John Doe</FirstName>
</User>
<User key="1">
<UserlID>101</UserlID>
<FirstName>Jane Doe</FirstName>
</User>
<User key="2">
<UserlID>102</UserlID>
<FirstName>Jack Black</FirstName>
</User>
</Individuals>
My desired result will be either, three rows of all key-s UserID-s or one row concatenating all three key UserID-s. the number of the key is variable, maybe more than three.
Have managed to get static UserID values:
SELECT
[UserID] = A.XmlField.value('(Individuals/User[#key=2]/UserlID)[1]', 'Int')
FROM [MyTable] As A
but the problem is I cannot get all the userID-s

You can do it this way:
SELECT u.value('UserlID[1]', 'int') as UserlID
FROM MyTable CROSS APPLY XmlField.nodes('/Individuals/User') i(u)

Related

Return specific nested node within XML from a SQL column

I need to return the value (Node) above the rows where 'Item' appears. This will be the column value for anything that appears as Key and Values. The other values (other nodes, not shown in this example) return back with the correct headings. Returning other nodes not under Item are fine.
I have created a CTE to extract the XML values from a column (CustomFieldXML) against the Client record that I am looking at. The way certain values are nested in the XML, my code works fine. The issue arises where the sub heading appears under 'Item'. How do I return each heading above the <item> node, but incorporate that into my CTE?
;WITH Clients_CTE_CastToXML AS
(
SELECT
C.ID,
C.Name,
C.Environment AS [Environment],
C.[CustomFieldsXml] AS 'x'
FROM Clients AS C WITH(NOLOCK)
WHERE id=5806
),
XML_Data AS
(
SELECT
[Id],
[Name],
x.y.value('local-name(..)', 'VARCHAR(MAX)') 'parentElementName',
x.y.value('local-name(.)', 'VARCHAR(MAX)') AS 'attributeName',
x.y.value('.', 'VARCHAR(MAX)') AS 'attributeValue'
FROM Clients_CTE_CastToXML AS c
CROSS APPLY x.nodes('//*[text()], //#*') AS x(y)
)
SELECT
'' AS [Id],
'' AS 'Source',
'' AS [Name],
D.parentElementName,
D.attributeName,
D.attributeValue
FROM XML_Data AS D
OPTION(RECOMPILE)
Example of what is returned...I need to pull any level above the heading for <item> (so pull out the node that says <DNA> or <RiskLevel>). Not sure what the syntax should be for that node.
<values>
<AuthorisingPartner>C</AuthorisingPartner>
<DNA>
<item>
<Key>No</Key>
<Value>No</Value>
</item>
</DNA>
<RiskLevel>
<item>
<Key>Normal</Key>
<Value>Normal</Value>
</item>
</RiskLevel>
</values>
Image: See the output here

SQL query for XML data

I have a SQL Server database table with a column called XML that contains XML data which is structured like this:
<Item xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://test/data">
<Roots>
<Root>
<Name>Field Name</Name>
<Value>Field Value</Value>
</Root>
<Root>
<Name>Field Name</Name>
<Value>Field Value</Value>
</Root>
</Roots>
I want to use T-SQL to get the Value where Name = Total. I have tried the following but it isn't returning any data:
SELECT [XML]
FROM [BusinessAccount]
WHERE [XML].value('(/Root/Name)[13]', 'VARCHAR(MAX)') LIKE '%Total%'
If anyone could tell me where I've gone wrong?
You are missing the required WITH XMLNAMESPACES for your XML and the path is incorrect.
If you want to bring back rows where the 13th element consists of the text Total you can use the below.
WITH XMLNAMESPACES (DEFAULT 'http://test/data')
SELECT [XML]
FROM [BusinessAccount]
WHERE 1 = [XML].exist('(/Item/Roots/Root/Name)[13][text() = "Total"]')
Otherwise you can add the WITH XMLNAMESPACES to your original query and fix the path there too.
You need to specify namespaces. You can then match <Name> and <Value> pairs and extract the contents of <Value> like so:
SELECT NameNode.value('declare namespace x="http://test/data"; (../x:Value)[1]', 'varchar(100)')
FROM [BusinessAccount]
CROSS APPLY [XML].nodes('declare namespace x="http://test/data"; //x:Root/x:Name') AS n(NameNode)
WHERE NameNode.value('.', 'varchar(100)') = 'Total'
Demo on db<>fiddle

How to get value from a node in XML via SQL Server

I've found several pieces of information online about this but I can't get it working for the life of me.
This is the XML I have:
I need to extract the ID & Name value for each node. There are a lot.
I tried to do this but it returns NULL:
select [xml].value('(/Alter/Object/ObjectDefinition/MeasureGroup/Partitions/Partition/ID)[1]', 'varchar(max)')
from test_xml
I understand the above would return only 1 record. My question is, how do I return all records?
Here's the XML text (stripped down version):
<Alter xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" AllowCreate="true" ObjectExpansion="ExpandFull">
<ObjectDefinition>
<MeasureGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>ts_homevideo_sum_20140430_76091ba1-3a51-45bf-a767-f9f3de7eeabe</ID>
<Name>table_1</Name>
<StorageMode valuens="ddl200_200">InMemory</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<Partitions>
<Partition>
<ID>123</ID>
<Name>2012</Name>
</Partition>
<Partition>
<ID>456</ID>
<Name>2013</Name>
</Partition>
</Partitions>
</MeasureGroup>
</ObjectDefinition>
</Alter>
You need something like this:
DECLARE #MyTable TABLE (ID INT NOT NULL, XmlData XML)
INSERT INTO #MyTable (ID, XmlData)
VALUES (1, '<Alter xmlns="http://schemas.microsoft.com/analysisservices/2003/engine" AllowCreate="true" ObjectExpansion="ExpandFull">
<ObjectDefinition>
<MeasureGroup xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>ts_homevideo_sum_20140430_76091ba1-3a51-45bf-a767-f9f3de7eeabe</ID>
<Name>table_1</Name>
<StorageMode valuens="ddl200_200">InMemory</StorageMode>
<ProcessingMode>Regular</ProcessingMode>
<Partitions>
<Partition>
<ID>123</ID>
<Name>2012</Name>
</Partition>
<Partition>
<ID>456</ID>
<Name>2013</Name>
</Partition>
</Partitions>
</MeasureGroup>
</ObjectDefinition>
</Alter>')
;WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/analysisservices/2003/engine')
SELECT
tbl.ID,
MeasureGroupID = xc.value('(ID)[1]', 'varchar(200)'),
MeasureGroupName = xc.value('(Name)[1]', 'varchar(200)'),
PartitionID = xp.value('(ID)[1]', 'varchar(200)'),
PartitionName = xp.value('(Name)[1]', 'varchar(200)')
FROM
#MyTable tbl
CROSS APPLY
tbl.XmlData.nodes('/Alter/ObjectDefinition/MeasureGroup') AS XT(XC)
CROSS APPLY
XC.nodes('Partitions/Partition') AS XT2(XP)
WHERE
ID = 1
First of all, you must respect and include the default XML namespace defined in the root of your XML document.
Next, you need to do a nested call to .nodes() to get all <MeasureGroup> and all contained <Partition> nodes, so that you can reach into those XML fragments and extract the ID and Name from them.
This should then result in something like this as output:

Read XML file to datatables using c# asp.net

I want to read XML file and bulk copy to database
My xml is like :
<products>
<product>
<id>1</id>
<name>product one</name>
<subproducts>
<subproduct>
<color>Red</color>
<stock>1</stock>
</subproduct>
<subproduct>
<color>Green</color>
<stock>2</stock>
</subproduct>
</subproducts>
<images>
<image>http://qwqeq.com</image>
<image>http://asdasd.com</image>
</images>
</product>
</products>
I want to get three datatables which are for products, subproducts and images and will try to bulk insert to the database.
How can i aschieve this ?
The way you have tried must be something like below,
DataSet objDataSet = new DataSet();
objDataSet.ReadXml("FilePath.xml");
It just groups the nodes and forms tables.
You need to do something which is explained well in the below link.
http://csharp.net-informations.com/xml/how-to-read-xml.htm
Hope this helps!
You can pass the XML into a stored procedure as XML parameter and shredd it there:
DECLARE #xml XML=
N'<products>
<product>
<id>1</id>
<name>product one</name>
<subproducts>
<subproduct>
<color>Red</color>
<stock>1</stock>
</subproduct>
<subproduct>
<color>Green</color>
<stock>2</stock>
</subproduct>
</subproducts>
<images>
<image>http://qwqeq.com</image>
<image>http://asdasd.com</image>
</images>
</product>
</products>';
SELECT p.value(N'id[1]',N'int') AS productID
,p.value(N'name[1]',N'nvarchar(max)') AS productName
,sp.value(N'color[1]','nvarchar(max)') AS subproductColor
,sp.value(N'stock[1]','int') AS subproductStock
,img.value(N'image[1]',N'nvarchar(max)') AS imageURL
--INTO #tmpTbl
FROM #xml.nodes(N'/products/product') AS A(p)
OUTER APPLY p.nodes(N'subproducts/subproduct') AS B(sp)
OUTER APPLY p.nodes(N'images') AS C(img)
The result
productID productName subproductColor subproductStock imageURL
1 product one Red 1 http://qwqeq.com
1 product one Green 2 http://qwqeq.com
Use SELECT ... INTO #tmpTbl to write the result into a staging table. Then use SELECT DISTINCT ... FROM #tmpTbl to retrieve the values for your insertion into the final structure.

Modify XML values identified through cross apply

I've got a data issue with some values stored in an XML column in a database. I've reproduced the problem as the following example:
Setup Script:
create table XMLTest
(
[XML] xml
)
--A row with two duff entries
insert XMLTest values ('
<root>
<item>
<flag>false</flag>
<frac>0.5</frac>
</item>
<item>
<flag>false</flag>
<frac>0</frac>
</item>
<item>
<flag>false</flag>
<frac>0.5</frac>
</item>
<item>
<flag>true</flag>
<frac>0.5</frac>
</item>
</root>
')
In the XML portion the incorrect entries are those with <flag>false</flag> and <frac>0.5</frac> as the value of flag should be true for non-zero frac values.
The following SQL identifies the XML item nodes that require update:
select
i.query('.')
from
XMLTest
cross apply xml.nodes('root/item[flag="false" and frac > 0]') x(i)
I want to do an update to correct these nodes, but I don't see how to modify the item elements identified by a cross apply. I saw the update as looking something like this:
update t
set
x.i.modify('replace value of (flag/text())[1] with "true"')
from
XMLTest t
cross apply xml.nodes('root/item[flag="false" and frac > 0]') x(i)
However this isn't working: I get the error "Incorrect syntax near 'modify'".
Can this be done through this method?
I know an alternative would be to do a string replace on the xml column, but I don't like that as being a bit unsubtle (and I'm not confident it wouldn't break things in my real-word problem)
It is not possible to update the one XML instance in more than one place at a time so you have to do the updates in a loop until you are done.
From http://msdn.microsoft.com/en-us/library/ms190675.aspx "Expression1: Identifies a node whose value is to be updated. It must identify only a single node."
-- While there are rows that needs to be updated
while exists(select *
from XMLTest
where [XML].exist('root/item[flag="false" and frac > 0]') = 1)
begin
-- Update the first occurence in each XML instance
update XMLTest set
[XML].modify('replace value of (root/item[flag="false" and frac > 0]/flag/text())[1] with "true"')
where xml.exist('root/item[flag="false" and frac > 0]') = 1
end