Parsing non-standard XML in SQL Server with XTbl - sql

I have a serialised piece of data in a column that I want to retrieve a single value from, in the following form:
<FirstNode>Something</><SecondNode>Something Else</>
I want to retrieve, for example, 'Something' from this in SQL Server. I've tried the following:
declare #data xml;
set #data = cast([my data source] as xml);
select #data.value('(/UserName)[1]','varchar(50)')
I'm probably way off with this, I don't have a huge deal of experience with parsing XML. Any help would be great.
Edit: I get the error
XML parsing: line 1, character 20, illegal qualified name character

Just use the CHARINDEX and SUBSTRING functions to get the data you want. Rolling my example into a function would probably be your best bet.
DECLARE #tbl TABLE(data VARCHAR(MAX))
INSERT INTO #tbl VALUES
('<FirstNode>Something</><SecondNode>Something Else</>'),
('<SecondNode>Something Else</><FirstNode>More Something</>'),
('<BadNoe>Something</><SecondNode>Something Else</>')
DECLARE #fnd VARCHAR(64)
DECLARE #end VARCHAR(64)
SET #fnd = '<FirstNode>'
SET #end = '</>'
SELECT SUBSTRING(a.[data], a.[start] + LEN(#fnd), a.[end] - (a.[start] + LEN(#fnd)))
FROM (SELECT data [data], CHARINDEX(#fnd, data, 0) [start], CHARINDEX(#end, data, CHARINDEX(#fnd, data, 0)) [end] FROM #tbl) a
WHERE a.[start] > 0

Related

How to not insert xmlns in SQL Server?

I need to add some XML elements into an XML column in SQL Server.
Here's a simplified example of my code:
DECLARE #temp XML = '<Rate>' + CONVERT(VARCHAR(20), #RateAmt, 1) + '</Rate>'
UPDATE [TableName]
SET [XMLValue].modify('declare namespace ns="http://www.mycompany.com/schema";insert sql:variable("#temp") as last into (/ns:Element1/ns:Element2)[1]')
WHERE id = #Id
Here's the output:
<Rate xmlns="">12.00</Rate>
So, the code is working, however, how do I remove the xmlns="" attribute?
Why are you inserting a namespace if you don't want one in the xml?
DECLARE #RateAmt decimal(9,2) = 12.00
DECLARE #temp XML = '<Rate>' + CONVERT(VARCHAR(20), #RateAmt, 1) + '</Rate>'
DECLARE #tempTable TABLE
(
Column1 Xml
)
INSERT INTO #tempTable(Column1)
SELECT #temp
OR
UPDATE #tempTable
SET Column1 = (SELECT #temp)
SELECT * FROM #tempTable
<Rate>12.00</Rate>
(1 row(s) affected)
There is an accepted answer already (especially concerning your namespace issue), great, just some hints:
There are very rare situation where one should create XML via string concatenation... Especially in connection with strings (special characters!), numbers (format!) and date/time values (culture and format!) it is much better to rely on the implicit translations using SELECT ... FOR XML ...
DECLARE #RateAmt DECIMAL(12,4)=12.0;
This is possible, but not good:
DECLARE #temp XML = '<Rate>' + CONVERT(VARCHAR(20), #RateAmt, 1) +'</Rate>'
Better try this
DECLARE #temp XML=(SELECT #RateAmt FOR XML PATH('Rate'));
Your attempt to insert this into existing XML can be done the way you do it already (create the XML-node externally and insert it as-is), it might be easier to insert the plain value:
DECLARE #tbl TABLE(ID INT IDENTITY,XMLValue XML);
INSERT INTO #tbl VALUES
(N'<Element1><Element2><test>FirstTest</test></Element2></Element1>')
,(N'<Element1><Element2><test>Second</test></Element2></Element1>');
--ID=1: Insert the way you do it:
UPDATE #tbl
SET [XMLValue].modify('insert sql:variable("#temp") as last into (/Element1/Element2)[1]')
WHERE id = 1
--ID=2: Insert the value of #RateAmt directly
SET #RateAmt=100.00;
UPDATE #tbl
SET [XMLValue].modify('insert <Rate>{sql:variable("#RateAmt")}</Rate> as last into (/Element1/Element2)[1]')
WHERE id = 2
This is Result ID=1
<Element1>
<Element2>
<test>FirstTest</test>
<Rate>12.0000</Rate>
</Element2>
</Element1>
And ID=2
<Element1>
<Element2>
<test>Second</test>
<Rate>100</Rate>
</Element2>
</Element1>

Easiest way to query a SQL Server 2008 R2 XML data type?

I need to get a node value in an XML data type column.
<CustomContentData>
<prpIsRSSFeed>false</prpIsRSSFeed>
</CustomContentData>
How is this done in SQL Server?
The column name is ClassXML
Use XQuery, a simple example with your data would be:
DECLARE #T TABLE (ClassXML XML);
INSERT #T (ClassXML)
VALUES ('<CustomContentData>
<prpIsRSSFeed>false</prpIsRSSFeed>
</CustomContentData>');
SELECT t.ClassXML.value('CustomContentData[1]/prpIsRSSFeed[1]', 'VARCHAR(5)')
FROM #T AS t;
If the column is already XML data type in SQL Server, then the code below should work by using the value function with XPATH. If it's stored as a varchar, you'd just need to replace ClassXML.value with CONVERT(XML, ClassXML).value. Hope this helps!
DECLARE #Data TABLE (ClassXML XML)
INSERT #Data VALUES ('<CustomContentData><prpIsRSSFeed>false</prpIsRSSFeed></CustomContentData>')
SELECT
CONVERT(BIT, CASE WHEN ClassXML.value ('(/CustomContentData/prpIsRSSFeed)[1]',
'VARCHAR(50)') = 'true' THEN 1 ELSE 0 END) AS IsRssFeed
FROM #Data
Yields output
IsRssFeed
---------
0

How to get the data between mth and nth occurrence in a string

I'm using a SQL Server query to fetch the column information. But I need some information which is after 3rd and 4th occurrence in that particular column
Here is my sample data
[xxxxxxx||gh||vbh||CAPACITY_CPU||aed]
[qwe34||asdf||qwe||CONNECTIVITY||ghj]
[ertgfy||fgv||yuhjj||ACCESS||rty]
[tyhuj||rtg||qwert||ACCESS||TMW]
I'm looking for the data information after 3rd and 4th occurrence of ||
Something like
Capacity_CPU
CONNECTIVITY
ACCESS
My source column is not specific length, it will vary in the length
Use PATINDEX
create regex for the column that you need, then use SUBSTRING to extract the string that you want
You can use mixture of SUBSTRING, CHARINDEX, LEFT AND RIGHT Function. The best solution is you have to play with this function.
`
Create table #t( Name varchar(200))
Insert into #t
values
('[xxxxxxx||gh||vbh||CAPACITY_CPU||aed]'),
('[qwe34||asdf||qwe||CONNECTIVITY||ghj]'),
('[ertgfy||fgv||yuhjj||ACCESS||rty]'),
('[tyhuj||rtg||qwert||ACCESS||TMW]')
Select * from #t
Select
name,
Right(LEFT(name,len(name)-6),charindex('||',reverse(LEFT(name,len(name)-7))))
From #t
`
1) Instead of trying to do such operations with those strings you could normalize database by designing and adding a new table. In this case, you would need a simple SELECT:
SELECT Column4
FROM dbo.Table;
2) Otherwise, one solution is to convert those strings into XML and to use nodes and value XML methods:
DECLARE #Source NVARCHAR(MAX);
SET #Source =
N'[xxxxxxx||gh||vbh||CAPACITY_CPU||aed]
[qwe34||asdf||qwe||CONNECTIVITY||ghj]
[ertgfy||fgv||yuhjj||ACCESS||rty]
[tyhuj||rtg||qwert||ACCESS||TMW]';
DECLARE #EncodedSource NVARCHAR(MAX);
SET #EncodedSource = (SELECT #source FOR XML PATH(''));
DECLARE #x XML;
SET #x = REPLACE(REPLACE(REPLACE(#EncodedSource, N'[', N'<row> <col>'), N']', N'"</col> </row>'), N'||', N'</col> <col>');
SELECT r.XmlContent.value('(col[1]/text())[1]', 'NVARCHAR(100)') AS Col1,
r.XmlContent.value('(col[4]/text())[1]', 'NVARCHAR(100)') AS Col4
FROM #x.nodes('/row') r(XmlContent);
Note: you need to replace NVARCHAR(length) with the proper data type and max. length.

SQL Server 2005: How to perform a split on a string

I have the following string that I need to split from a field called symbols
234|23|HC
This is my current SQL statement
declare #t xml;
Set #t = (
Select symbols from tc for xml auto, elements)
Select #t;
which produces <symbols>234|23|HC</symbols>
but I need to split the string into child nodes so the result is like this:
<symbols>
<symbol>234</symbol>
<symbol>23</symbol>
<symbol>HC</symbol>
</symbols>
A replace version that takes care of the problem characters.
declare #T table(symbol varchar(50))
insert into #T values ('234|23|HC|Some problem chars <> &')
select cast('<symbols><symbol>'+
replace(cast(cast('' as xml).query('sql:column("symbol")') as varchar(max)),
'|',
'</symbol><symbol>')+
'</symbol></symbols> ' as xml)
from #T
Result:
<symbols>
<symbol>234</symbol>
<symbol>23</symbol>
<symbol>HC</symbol>
<symbol>Some problem chars <> &</symbol>
</symbols>

how to get values inside an xml column, when it's of type nvarchar

My question is similar to this one: Choose a XML node in SQL Server based on max value of a child element
except that my column is NOT of type XML, it's of type nvarchar(max).
I want to extract the XML node values from a column that looks like this:
<Data>
<el1>1234</el1>
<el2>Something</el2>
</Data>
How can I extract the values '1234' and 'Something' ?
doing a convert and using the col.nodes is not working.
CONVERT(XML, table1.col1).value('(/Data/el1)[1]','int') as 'xcol1',
After that, I would like to do a compare value of el1 (1234) with another column, and update update el1 as is. Right now I'm trying to just rebuild the XML when passing the update:
ie
Update table set col1 ='<Data><el1>'+#col2+'</el1><el2>???</el2>
You've got to tell SQL Server the number of the node you're after, like:
(/Data/el1)[1]
^^^
Full example:
declare #t table (id int, col1 varchar(max))
insert #t values (1, '<Data><el1>1234</el1><el2>Something</el2></Data>')
select CAST(col1 as xml).value('(/Data/el1)[1]', 'int')
from #t
-->
1234
SQL Server provides a modify function to change XML columns. But I think you can only use it on columns with the xml type. Here's an example:
declare #q table (id int, col1 xml)
insert #q values (1, '<Data><el1>1234</el1><el2>Something</el2></Data>')
update #q
set col1.modify('replace value of (/Data/el1/text())[1] with "5678"')
select *
from #q
-->
<Data><el1>5678</el1><el2>Something</el2></Data>
At the end of the day, SQL Server's XML support makes simple things very hard. If you value maintainability, you're better off processing XML on the client side.