TSql xml query is concatenating values - sql

When I query an xml column in a temp variable, the results are being concatenated if an xml element has multiple values. Below is an excerpt from the xml column-
<metadata>
<dataType>Date</dataType>
<tags>
<tag>SPA</tag>
<tag>Timed Release</tag>
</tags>
</metadata>
Notice the two tag elements. I need one row for each tag element. Here is my query-
SELECT id,
dataType = [Type].value('(/metadata/tags)[1]', 'varchar(max)')
FROM
#tmpProperty
that produces the below
id | dataType
-----+-------------------
6357 | SPATimed Release
if I change the query to
SELECT id,
dataType = [Type].value('(/metadata/tags/tag)[1]', 'varchar(max)')
FROM
#tmpProperty
I get
id | dataType
-----+-----------
6357 | SPA
Both results are wrong. One concatenates into a single row instead of 2 rows, and the other just returns one value. I am using sql server 2012. How can I fix this?

You need to use nodes() first to get multiple rows, then you can use value() to extract the data:
DECLARE #tmpProperty TABLE (ID INT IDENTITY, [Type] XML);
INSERT #tmpProperty([Type])
VALUES ('<metadata>
<dataType>Date</dataType>
<tags>
<tag>SPA</tag>
<tag>Timed Release</tag>
</tags>
</metadata>');
SELECT id,
dataType = Tags.value('(text())[1]', 'varchar(max)')
FROM #tmpProperty AS t
CROSS APPLY t.[Type].nodes('/metadata/tags/tag') AS n (Tags);

Related

XML column Data into rows

I have a requirement where I have XML data column in database, which I need to pull in the form of values from rows XML data column from database. My XML is like
<ListID><ID>169346</ID><ID>289492</ID><ID>315264</ID><ID>415265</ID></ListID>
<ListID><ID>169356</ID><ID>299492</ID><ID>315264</ID><ID>415265</ID></ListID>
And I want data to be pulled up like
ID
169346
289492
315264
415265
169356
299492
315264
415265
You can use something like this:
SELECT XC.value('.', 'int')
FROM dbo.YourTableHere
CROSS APPLY XmlColumn.nodes('//ID') AS XT(XC)
This basically takes every <ID> element that exists in the XML column, and extract the values as int and shows them in a result set.
Update: from your question, mentioning I have XML data column, I assumed that your column in the SQL Server table is in fact of type XML. And it should be, if you're storing XML in it!
But if it's not - then you need to cast your column to XML first, before using the function .nodes() in my code sample:
SELECT XC.value('.', 'int')
FROM dbo.YourTableHere
CROSS APPLY CAST(YourColumn AS XML).nodes('//ID') AS XT(XC)
Here is one other way is to convert it to XML & then convert it to row :
SELECT split.a.value('.', 'varchar(max)') ID
FROM
(
SELECT CAST(ID AS XML) AS String from <table_name>
) a
CROSS APPLY String.nodes('//ID') AS split(a);
Result :
ID
169346
289492
315264
415265
169356
299492
315264
415265

Shred XML For Each Row in SQL Table

I have a table that contains two columns, and ID, and XML data. I'd like to shred the XML for each ID. I'm pulling out a single value within the XML and all the XML is structured the same I'm just not sure how to loop through the table and apply XML query to each row.
The query I need to apply is as follows:
Select top 1
Element1 = XV.value('(.)[1]','nvarchar(32)')
from #xml.nodes('Parameters/Parameter/Value') as x(XV)
So the end results would have two columns, ID and shredded value from XML.
Without any knowledge about your actual XML and how you want to shred it to get some values it is impossible to answer in completness, but this shoudl point you in the right direction:
Returns the ID and the XML as is
SELECT ID
,TheXmlColumn
FROM YourTable
This returns the ID and a value out of your XML
SELECT ID
,TheXmlColumn.value('Some XPaht','SomeType') AS SomeValueFromXML
FROM YourTable
And if there are more embedded rows it would be something like this
SELECT ID
,nd.value('Some XPaht','SomeType') AS SomeValueFromXMLRow
FROM YourTable
OUTER APPLY TheXmlColumn.nodes('SomeXPath') AS A(nd)
My magic glass bulb tells me, that you might need something like this:
SELECT ID
,TheXmlColumn.value('(Parameters/Parameter/Value)[1]','nvarchar(max)') AS SomeValueFromXML
FROM YourTable

SQL: Select records that have XML tag

I would like to ask how to select only those records from a table that has a column with XML, that in their XML contain a specific tag.
Table name is: Searchindex
Column with XML: Record
XML element:
The tag is included only on a few records, not all.
XML sample structure:
<Record>
<ppn>asasaa</ppn>
<NAM>asdad</NAM>
<HasProduct>True</HasProduct> << this tag is only on certain records
<ART>asdadsa</ART>
<PublicationDate>06/21/1999</PublicationDate>
<PSal>1305</PSal>
<MSal>14</MSal>
<Xpos>False</Xpos>
</Record>
Any examples would be great.
Thanks a lot!
Here is example:
CREATE TABLE T
(
ID int,
X xml
)
INSERT INTO T VALUES
(1, '<Record>
<ppn>asasaa</ppn>
<NAM>asdad</NAM>
<HasProduct>True</HasProduct>
<ART>asdadsa</ART>
<PublicationDate>06/21/1999</PublicationDate>
<PSal>1305</PSal>
<MSal>14</MSal>
<Xpos>False</Xpos>
</Record>'),
(2, '<Record>
<Xpos>False</Xpos>
</Record>')
SELECT *
FROM T
WHERE X.exist('Record/HasProduct')=1
/*Tag with value=True*/
SELECT *
FROM T
WHERE X.exist('Record/HasProduct[text()="True"]')=1
try
SELECT * FROM table1
WHERE x REGEXP '<HasProduct>\w+</HasProduct>'
\w can be change to any search value you want

Compare Xml data in SQL

I have two tables with same NVARCHAR field that really contains XML data.
in some cases this really-XML-field is really same as one row in other table but differs in attributes order and therefor string comparison does not return the correct result!!!
and to determining the same XML fields ,I need to have a comparison like:
cast('<root><book b="" c="" a=""/></root>' as XML)
= cast('<root><book a="" b="" c=""/></root>' as XML)
but I get this Err Msg:
The XML data type cannot be compared or sorted, except when using the
IS NULL operator.
then what is the best solution to determine the same XML without re-casting them to NVARCHAR?
Why cast it at all? Just plug them into an XML column in a temp table and run Xquery to compare them to the other table. EDIT: Included example of the comparison. There are many, many ways to run the query against the XML to get the rows that are the same - exactly how that query is written is going to depend on preference, requirements, etc. I went with a simple group by/count, but a self join could be used, WHERE EXISTS against the columns that are being searched for duplicates, you name it.
CREATE TABLE #Test (SomeXML NVARCHAR(MAX))
CREATE TABLE #XML (SomeXML XML)
INSERT #Test (SomeXML)
VALUES('<root><book b="b" c="c" a="a"/></root>')
,('<root><book a="a" b="b" c="c"/></root>')
INSERT #XML (SomeXML)
SELECT SomeXML FROM #Test;
WITH XMLCompare (a,b,c)
AS
(
SELECT
x.c.value('#a[1]','char(1)') AS a
,x.c.value('#b[1]','char(1)') AS b
,x.c.value('#c[1]','char(1)') AS c
FROM #XML
CROSS APPLY SomeXMl.nodes('/root/book') X(C)
)
SELECT
a
,b
,c
FROM XMLCompare as a
GROUP BY
a
,b
,c
HAVING COUNT(*) >1

Filter SQL queries on the XML column using XPath/XQuery

I'm having a table with one XML column. I'd like to filter out the rows where a specific attribute in the XML match a string, essentially doing a WHERE or HAVING.
The table looks something like this
| id | xml |
And the XML something similar to
<xml>
<info name="Foo">
<data .../>
</info>
<xml>
I want to get all ids where the #name attribute matched a value.
I have been able to do the following:
SELECT id, xml.query('data(/xml/info/#name)') as Value
FROM Table1
WHERE CAST(xml.query('data(/xml/info/#name)') as varchar(1024)) = #match
But it's incredibly slow.
There must be a better way of filtering on the output of the query.
Found it. Instead of using query() I should be using exist().
My query would then be
SELECT id, xml.query('data(/xml/info/#name)') as Value
FROM Table1
WHERE xml.exist('/xml/info/[#name=sql:variable("#match")]') = 1