Seperate XML node by comma in SQL - sql

I have a Column content like this:
CustomTags
<CustomTagsSerialiser>
<custom-tags>
<tag>Visas and travel</tag>
<tag>Explore Options</tag>
<tag>Consider – feasibility</tag>
</custom-tags>
</CustomTagsSerialiser>
I can query g.[CustomTags].value('(/CustomTagsSerialiser//custom-tags)[1]', 'nvarchar(500)') as Custom_Tag to get result like
Visas and travelExplore OptionsConsider – feasibility
But I want the result to have a tag separated by comma (in the same column), like the following:
Visas and travel,Explore Options,Consider – feasibility
Ideally, I would like this to be implemented by using XML functionality/node
instead of breaking it into + ',' + or coalesce

You may refere How Stuff and 'For Xml Path' work in Sql Server this answer.
try below
SELECT
STUFF((SELECT
',' + CTS.tag.value('(.)[1]', 'nvarchar(500)')
FROM
Temp12345
CROSS APPLY
col1.nodes('/CustomTagsSerialiser/custom-tags/tag') AS CTS(tag)
FOR XML PATH('')
), 1, 1, '')

should be this without using cross apply
STUFF((SELECT ',' + x.t.value('.', 'varchar(50)') FROM
[g].CustomTags.nodes('//tag') x(t) FOR XML PATH('')), 1, 1, '') AS 'Custom Tags'

Related

Why does using XML Path('') return a blue underlined data and how to remove it?

I am using this to concatenate different strings into one and it works but problem is that it is UNDERLINED and BLUE like a link and I tried removing it but nothing works. Also, column header is something not presentable.
SELECT np.Name+ ', ' AS 'data()'
FROM NewsPaper np
inner join NitNewsPaper nitnp
on nitnp.NewsPaper_ID= np.NewPaperID
where Nit_NO= 1518
FOR XML PATH('')
Try the below query. It should fix your issue.
SELECT STUFF((SELECT ', ' + np.NAME
FROM NewsPaper np
INNER JOIN NitNewsPaper nitnp ON nitnp.NewsPaper_ID = np.NewPaperID
WHERE Nit_NO = 1518
FOR XML PATH('')), 1, 2, '') as 'data'
FOR XML PATH allows you to output the results of the query as XML and hence you are seeing the blue underline. This will output something like: , Name1, Name2. We need to get rid of the leading ', ' from this. So, we use STUFF to remove the first 2 characters.

Delete spaces and special characters

This question is linked to :concatenate a column on one line depends on a id
I used this query (thanks Mihai) :
SELECT id, date,MAX(docline),
Ids=Stuff((SELECT ' ' + doctext FROM documentation d WHERE d.id=documentation.id
FOR XML PATH (''))
, 1, 1, '' )
from documentation where date in (02/14/2017)
GROUP BY id,date
I received this line :
Backup :
D:\Bas.bac
test testtest
tesdttest testtest
I would like to have something like :
Backup : D:\Bas.bac test testtest tesdttest testtest
So no space and no special characters like ('
')
Like you can see I have some special characters and too much space...
I tried to use RTrim() and LTRIM like :
SELECT id, date,MAX(docline),
Ids=Stuff(RTRIM(SELECT ' ' + doctext FROM documentation d WHERE d.id=documentation.id
FOR XML PATH ('')))
, 1, 0, '' )
from documentation where date in (02/14/2017)
GROUP BY id,date
But I don't see any difference and I have the same behavior.
Thanks,
EDIT : I tried too (without success) :
Stuff((SELECT ' ' + LTRIM(RTRIM(doctext)) FROM documentation d WHERE d.id=documentation.id FOR XML PATH ('')),1,0,'')
If you just need to remove multiple spaces you could try
REPLACE (<string>,' ', '')
But in your case it also remove double space between Backup and :

Using CHAR(13) in a FOR XML SELECT

I'm trying to use CHAR(13) to force a new line, but the problem is, I'm doing this within a FOR XML Select statement:
SELECT
STUFF((SELECT CHAR(13) + Comment
FROM
myTable
FOR XML PATH ('')) , 1, 1, '')
The problem with this, is that I don't get an actual new line. Instead, I get:
#x0D;
So the data literally looks like this:
#x0D;First Line of Data#x0D;Second Line of Data#x0D;Etc
So I tried to just replace #x0D; with CHAR(13) outside of the FOR XML:
REPLACE(SELECT
STUFF((SELECT CHAR(13) + Comment
FROM
myTable
FOR XML PATH ('')) , 1, 1, '')), '#x0D;', CHAR(13))
This gets me close. It DOES add in the line breaks, but it also includes an & at the end of each line, and the start of each line after the first:
First Line of Data&
&Second Line of Data&
&Etc
Your approach is not real XML:
Try this with "output to text":
DECLARE #tbl TABLE(TestText VARCHAR(100));
INSERT INTO #tbl VALUES('line 1'),('line 2'),('line 3');
SELECT STUFF
(
(
SELECT CHAR(10) + tbl.TestText
FROM #tbl AS tbl
FOR XML PATH('')
),1,1,''
)
With CHAR(13)
#x0D;line 1
line 2
line 3
See that your STUFF just took away the ampersand?
With CHAR(10)
line 1
line 2
line 3
But what you really need is:
SELECT STUFF
(
(
SELECT CHAR(10) + tbl.TestText --you might use 13 and 10 here
FROM #tbl AS tbl
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)'),1,1,''
)
The ,TYPE will return real XML and with .value() you read this properly.
Some background
You have a misconception of "So the data literally looks like this"
It does not "look like this", it is escaped to fit to the rules within XML. And it will be back encoded implicitly, when you read it correctly.
And you have a misconception of line breaks:
In (almost) ancient times you needed a CR = Carriage Return, 13 or x0D to move back the printing sledge and additionally you needed a LF = Line Feed, 10 or x0A to turn the platen to move the paper. Hence the widely used need to have a line break coded with two characters (13/10 or 0D/0A).
Today the ASCII 10 (0A) is often seen alone...
But back to your actual problem: Why do you bother about the look of your data? Within XML some string might look ugly, but - if you read this properly - the decoding back to the original look is done implicitly...
Your residues are not more than part of the encoding as this starts with an ampersand and ends with a semicolon: &lg; or 
. Your attempt to replace this is just one character to short. But anyway: You should not do this!
Just try:
SELECT CAST('<x>Hello</x>' AS XML).value('/x[1]','nvarchar(max)')
Thanks everyone for your help.
The ultimate goal here was to present the data in Excel as part of a report. I'm sure there is a more elegant way to do this, but I at least got the results I wanted by doing this:
REPLACE (
REPLACE(
REPLACE(
(SELECT Comment FROM CallNotes WHERE ForeignId = a.ForeignId FOR XML PATH (''))
, '<Comment>', '')
, '</Comment>', CHAR(13) + CHAR(10))
, '
', '') AS Comments
The select statement all by itself returns XML as we would expect:
<comment>This is a comment</comment><comment>This is another comment</comment>
The inner most REPLACE just gets rid of the opening tag:
<comment>
The middle REPLACE removes the closing tag:
</comment>
and replaces it with CHAR(13) + CHAR(10). And the outer most REPLACE gets rid of this:
(I still don't understand where that's coming from.)
So, when the results are sent to Excel, it looks like this inside the cell:
This is a comment.
This is another comment.
Which is exactly what I want. Again, I'm sure there is a better solution. But this at least is working for now.
I think this is cleaner. Basically start with line feeds (or some other special character) then replace them with carriage returns plus line feeds if you want.
Select REPLACE(STUFF((SELECT CHAR(10) + Comment
FROM myTable FOR XML PATH ('')) , 1, 1, ''),
CHAR(10), CHAR(13)+CHAR(10))
I suppose since you need to group on the FK you can use something like this... just replace #TempT with your table...
Select Pri.ForeignKey,
Replace(Left(Pri.Notes,Len(Pri.Notes)-1),',',CHAR(13)) As Notes
From
(
Select distinct T2.ForeignKey,
(
Select T1.Note + ',' AS [text()]
From #TempT T1
Where T1.ForeignKey = T2.ForeignKey
ORDER BY T1.ForeignKey
For XML PATH ('')
) Notes
From #TempT T2
) Pri
Also in the OP that you listed in the comments, you have a duplicate PrimaryKey. I found that odd. Just a heads up.
If you use below query and results to text option you will see line breaks. Line breaks can't be shown using the results to grid functionality.
SELECT
STUFF((SELECT CHAR(10) + Comment
FROM
myTable
FOR XML PATH ('')) , 1, 1, '')
I suggest Comment + Char(10) + Char(13)
The "Carriage Return" "Line feed" should be at the end of the line.
I believe this could help:
REPLACE(STUFF ((SELECT CHAR(13)+CHAR(10) + Field1 + Field2
FROM
((table
WHERE
field3= 'condition1'
FOR XML PATH ('')), 1, 0, '') , '
' , '')

Concatenate format to simple format

I have several data concatenated in one cell delimited by "," separator. Below is the screen shot for data and output i required. I know how to convert from output to concatenate format by using For XML but i am unable to convert concatenate to the output format.
I am using Sql server 2008. Kindly help to accomplish this.
Regards,
Ratan
as you have composite string in both columns then I prefer to use cross joins with convert to xml, twice :
SELECT MemberId = y.i.value('(./text())[1]', 'nvarchar(1000)'),
TokenId = u.j.value('(./text())[1]', 'nvarchar(1000)')
FROM
(
SELECT
m = CONVERT(XML, '<i>'
+ REPLACE(MemberId, ',' , '</i><i>')
+ '</i>').query('.'),
t= CONVERT(XML, '<j>'
+ REPLACE(TokenId, ',' , '</j><j>')
+ '</j>').query('.')
FROM member_tokens
) AS a
CROSS APPLY m.nodes('i') AS y(i)
CROSS APPLY t.nodes('j') AS u(j)
SQLFIDDLE DEMO

SQL Server : xml string to rows

I'm trying to convert a string to rows using T-SQL. I've found some people using XML for this but I'm running into troubles.
The original record:
A new line seperated string of data
New In Progress Left Message On Hold Researching Researching (2nd Level) Researching (3rd Level) Resolved Positive False Positive Security Respond
Using the following statement converts this string into XML:
select
cast('<i>'+REPLACE(convert(varchar(max), list_items), CHAR(13) + CHAR(10),'</i><i>')+'</i>' as xml)
from
field
where
column_name = 'state' and table_name = 'sv_inquiry'
XML string:
<i>Unassigned</i><i>Assigned</i><i>Transferred</i><i>Accepted</i><i>Closed</i><i>Reactivated</i>
Now I would like to convert every 'i' node into a separate row. I've constructed the query below, but I can't get it working in the way that it returns all the rows...
select x.i.value('i[1]', 'varchar(30)')
from (
select cast('<i>'+REPLACE(convert(varchar(max), list_items), CHAR(13) + CHAR(10),'</i><i>')+'</i>' as xml)
from field
where column_name='state' and table_name='sv_inquiry'
) x(i)
This will return
Unassigned
To be clear, when i change 'i[1]' into 'i[2]' it will return 'Assigned'. I've tried '.', this will return the whole string in a single record...
How about using the nodes method on an XML datatype.
declare #xml xml
set #xml = '<i>Unassigned</i><i>Assigned</i><i>Transferred</i><i>Accepted</i><i>Closed</i><i>Reactivated</i>'
select
t.c.value('.', 'nvarchar(100)') as [Word]
from
#xml.nodes('/i') as t(c)
You can split a string into rows without XML, see for example the fnSplitString function at SQL Server Central.
Here's an example using the nodes() function of the xml type. I'm using a space as the delimiter because SQL Fiddle doesn't play well with line feeds:
select node_column.value('.', 'varchar(max)')
from (
select cast('<i>' + replace(list_items, ' ', '</i><i>') +
'</i>' as xml) xml_value
from field
) f
cross apply
xml_value.nodes('/i') node_table(node_column);
Live example at SQL Fiddle.