Remove trailing comma from xml path - sql

How do I remove the trailing comma from the result of this query:
SELECT CONVERT(varchar(10), clientid) + ','
FROM daily
FOR XML PATH('')
The output is 0,0,351,350,
This is not a duplicate. The other questions are not using xml path, but I am.

Usually you use the FOR XML PATH query as a sub query and put the comma at the beginning instead of end so that it is easier to STUFF:
SELECT STUFF((
SELECT ',' + CONVERT(varchar(10), clientid)
FROM daily
FOR XML PATH('')
), 1, 1, '')

Works in SQL Server
SELECT LEFT(CONVERT(varchar(10), clientid) + ',', LEN(CONVERT(varchar(10), clientid) + ',') - 1)
FROM daily
FOR XML PATH('')

Related

Remove leading comma from FOR XML PATH function

I created a variable that accepts multiple string values using the FOR XML PATH:
set #Control_Number =
(SELECT DISTINCT
SUBSTRING(
(
SELECT ''', '''+ co.control_number AS [text()]
FROM #AGBCaseCompanyMap2 co
WHERE co.company_id = co2.company_id
ORDER BY co.company_id
FOR XML PATH ('')
), 2, 1000) [control_number]
The result I'm getting is including a leading comma: (', '0045', '4343').
I've used the STUFF function in the past to remove this but I can't figure out how to use it here. I keep getting errors like "STUFF function requires 4 values." Does anyone know how to remove that leading quote and comma?
You just need to add the quotes separately with STUFF in the query:
set #Control_Number =
(SELECT DISTINCT
STUFF(
(
SELECT ', '+ '''' + co.control_number + '''' AS [text()]
FROM #AGBCaseCompanyMap2 co
WHERE co.company_id = co2.company_id
ORDER BY co.company_id
FOR XML PATH ('')
), 1, 2, '') [control_number]

Concat column values with comma as the separator

I am trying to figure out the result of the below query on how to concat all the columns into a single string with comma separated values. The number of values can be dynamic.
SELECT 'Sc4','Sc5','Sc8','Sc7','Sc2'
I want the result to be as below:
Sc4,Sc5,Sc8,Sc7,Sc2
I have tried using stuff but did manage to concat the string but unable to insert comma in between.
Below is what i have tried
SELECT Stuff((SELECT 'Sc4','Sc5','Sc8','Sc7','Sc2' FOR XML PATH('')), 2, 0, '');
UPDATE
I would like to rephrase my question, is there any way the out shown below can be converted to csv
i assume Sc4, Sc5 etc are your column name and not string constant ?
SELECT Stuff(
(
SELECT ',' + Sc4 + ',' + Sc5 + ',' + Sc8 + ',' + Sc7
+ ',' + Sc2
FROM yourtable
FOR XML PATH('')
), 1, 1, '');
Try concatenation like
SELECT 'Sc4' + ',' + 'Sc5' + ',' + 'Sc8' + ',' + 'Sc7' + ',' + 'Sc2'

Concatenation of strings by for xml path

Hi!
Today I learned for xml path technique to concatenate strings in mssql. Since I've never worked with xml in mssql and google hasn't helped, I need to ask you.
Let's imagine the default case. We need to concatenate some strings from a table:
declare #xmlRepNames xml = (
select
', [' + report_name + ']'
from (
select distinct
report_order,
report_name
from #report
) x
order by
report_order
for xml path(''), type)
select
stuff((select #xmlRepNames.value('.', 'nvarchar(max)')), 1, 1, '')
So I get smth like this:
[str1], [str2], [strn]
Ok. It works fine. But I have two very similar concatenate blocks. The difference is just in the way the result string looks like:
[str1], [str2], [strn]
and
isnull([str1], 0) as [str1], isnull([str2], 0) as [str2], isnull([strn], 0) as [strn]
So I can write 2 very similar code blocks (already done, btw) with different string constructors or to try extend previous code to has xml variable containing 2 kind of constructors and then concatenate by xml node type. Doing 2nd way I met some problems - I wrote this:
declare #xmlRepNames xml = (
select
', [' + report_name + ']' as name,
', isnull([' + report_name + '], 0) as [' + report_name + ']' as res
from (
select distinct
report_order,
report_name
from #report
) x
order by
report_order
for xml path(''), type)
select
stuff((select #xmlRepNames.value('/name', 'nvarchar(max)')), 1, 1, ''),
stuff((select #xmlRepNames.value('/res', 'nvarchar(max)')), 1, 1, '')
but it raise error "XQuery [value()]: 'value()' requires a singleton (or empty sequence), found operand of type 'xdt:untypedAtomic *'".
To replace, e.g., '/name' to '/name[1]' or any other '/name[x]', will return just x-th 'name' record but not all 'name' records concatenated.
[question]: is it possible to solve problem 2nd way like I want and if it's possible then how?
[disclaimer]: the problem isn't really serious for me now (1st way just a little bit uglier but also fine), but it seems very interesting how to come over :)
Thanks!
Your subquery cannot return two values. If you just want to concatenate strings, you do not need the xml data type at all. You can do the stuff() and subquery in a single statement:
declare #Rep1Names nvarchar(max) = (
stuff((select ', [' + report_name + ']' as name
from (select distinct report_order, report_name
from #report
) x
order by report_order
for xml path('')
)
), 1, 1, '');
declare #Rep2Names nvarchar(max) = (
stuff(select ', isnull([' + report_name + '], 0) as [' + report_name + ']' as res
from (select distinct report_order, report_name
from #report
) x
order by report_order
for xml path('')
)
), 1, 1, '');
Ok, so I haven't been completely satisfied the way Gordon Linoff suggested and since I've found this kind of problem actual for me I'm adding here another solution without using for xml path:
declare
#pivot_sequence nvarchar(max),
#columns_sequence nvarchar(max)
select
#pivot_sequence = coalesce(#pivot_sequence + ', [', '[')
+ col_name + ']',
#columns_sequence = coalesce(#columns_sequence + ', ', '')
+ 'isnull([' + col_name + '], 0) as [' + col_name + ']'
from some_table
order by
/* some_columns if needed to order concatenation */
Obviously, it'll work much slower but in cases when you haven't many rows it won't drastically affect on performance. In my case I have dynamic pivot query and these strings are built for it - I won't have many columns in pivot so it's fine for me.

SQL Query to List

I have a table variable in a stored procedure. What I want is to find all of the unique values in one column and join them in a comma-separated list. I am already in a stored procedure, so I can do it some way that way; however, I am curious if I can do this with a query. I am on SQL Server 2008. This query gets me the values I want:
SELECT DISTINCT faultType FROM #simFaults;
Is there a way (using CONCAT or something like that) where I can get the list as a single comma-separated value?
This worked for me on a test dataset.
DECLARE #MyCSV Varchar(200) = ''
SELECT #MyCSV = #MyCSV +
CAST(faulttype AS Varchar) + ','
FROM #Simfaults
GROUP BY faultType
SET #MyCSV = LEFT(#MyCSV, LEN(#MyCSV) - 1)
SELECT #MyCSV
The last part is needed to trim the trailing comma.
+1 to JNK - the other common way you will see, which doesn't require a variable is:
SELECT DISTINCT faulttype + ','
FROM #simfaults
FOR XML PATH ('')
Note that if faulttype contains characters like "<" for example, those will be xml encoded. But for simple values this will be OK.
this is how we do this
create table #test (item int)
insert into #test
values(1),(2),(3)
select STUFF((SELECT ', ' + cast(Item as nvarchar)
FROM #test
FOR XML PATH('')), 1, 2, '')
Without the space after the comma it would be;
select STUFF((SELECT ',' + cast(Item as nvarchar)
FROM #test
FOR XML PATH('')), 1,1, '')

TSQL Reverse FOR XML Encoding

I am using FOR XML in a query to join multiple rows together, but the text contains quotes, "<", ">", etc. I need the actual character instead of the encoded value like """ etc. Any suggestions?
Basically what you're asking for is invalid XML and luckly SQL Server will not produce it. You can take the generated XML and extract the content, and this operation will revert the escaped characters to their text representation. This revert normally occurs in the presnetaitonlayer, but it can occur in SQL Server itslef by instance using XML methods to extract the content of the produced FOR XML output. For example:
declare #text varchar(max) = 'this text has < and >';
declare #xml xml;
set #xml = (select #text as [node] for xml path('nodes'), type);
select #xml;
select x.value(N'.', N'varchar(max)') as [text]
from #xml.nodes('//nodes/node') t(x);
I have a similar requirement to extract column names for use in PIVOT query.
The solution I used was as follows:
SELECT #columns = STUFF((SELECT '],[' + Value
FROM Table
ORDER BY Value
FOR XML PATH('')), 1, 2, '') + ']'
This produces a single string:
[Value 1],[Value 2],[Value 3]
I hope this points you in the right direction.
--something like this?
SELECT * INTO #Names FROM (
SELECT Name='<>&' UNION ALL
SELECT Name='ab<>'
) Names;
-- 1)
SELECT STUFF(
(SELECT ', ' + Name FROM #Names FOR XML PATH(''))
,1,2,'');
-- 2)
SELECT STUFF(
(SELECT ', ' + Name FROM #Names FOR XML PATH(''),TYPE).value('text()[1]','nvarchar(max)')
,1,2,'');
-- 2) is slower but will not return encoded value.
Hope it help.