SQL Query to List - sql

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, '')

Related

Need to add double quotes to SQL SERVER string values

I have integer values that are being passed from a parameter that needed to be input as a string and padded so that they are 7 digits. This will then be passed into another query.
declare #t table(ctl_num int)
insert into #t values(5675, 45464, 2323)
declare #control_num varchar(500)
set #control_num = (select stuff((select ',' + right('000' + cast(ctl_num as varchar),7)
from #t
FOR XML PATH('')),1, 1', ''))
This codes sets #control_num as ('0005675, 0045464, 0002323'). I need this to be passed as ("0005675", "0045464", "0002323").
I've looked at other examples on-line but I can't seem to get this to work. Does anyone know how to get the double quotes around each value?
I think there is some issue in setting #control_num.
Please try the following:
set #control_num = (select stuff((select ',"' + right('000' + cast(ctl_num as varchar),7) + '"'
from #t
FOR XML PATH('')),1, 1, ''))
I would suggest:
select string_agg(concat('"', right('000' + cast(ctl_num as varchar(255)), 7), '"', ',')
from #t;
string_agg() has been available since SQL Server 2017. Also note that you should always include a length when referring to strings in SQL Server.

SQL for concatenating strings/rows into one string/row? (How to use FOR XML PATH with INSERT?)

I am concatenating several rows/strings in an table (on Microsoft SQL Server 2010) into a string by using a method as suggested here:
SELECT ',' + col FROM t1 FOR XML PATH('')
However, if I try to insert the resulting string as (single) row into another table like so:
INSERT INTO t2
SELECT ', ' + col FROM t1 FOR XML PATH('')
I receive this error message:
The FOR XML clause is not allowed in a INSERT statement.
t2 currently has a single column of type NVARCHAR(80). How can I overcome this problem, i.e. how can I collapse a table t1 with many rows into a table t2 with row that concatenates all the strings from t1 (with commas)?
Rather than xml path why not do it like this?
DECLARE #Cols VARCHAR(8000)
SELECT #Cols = COALESCE(#Cols + ', ', '') +
ISNULL(col, 'N/A')
FROM t1
Insert into t2 values(#Cols);
You need to cast it back to an nvarchar() before inserting. I use this method, deletes the first separator as well and as I'm doing the , type part, it handles entities correctly.
insert into t2
select stuff((
select ', ' + col from t1
for xml path(''), type
).value('.', 'nvarchar(80)'), 1, 2, '')
So you concatenate all col with prepending comma+space as an xml-object. Then you take the .value() of child with xquery-path . which means "take the child we are at, don't traverse anywhere". You cast it as an nvarchar(80) and replace a substring starting at position 1 and length 2 with an empty string ''. So the 2 should be replaced with however long your separator is.

Get values from XML tags with dynamically specified data fields

I have 2 tables:
Table1 has a list of XML tag names that I want to extract from an XML field. I simulate this by running this query
SELECT
'CLIENT'
UNION SELECT
'FEE'
UNION SELECT
'ADDRESS'
This results in a single column with 3 rows in it, the names of which will be used to extract corresponding data from XML tags.
The second table has a column called ClientData, it is in XML format and it has thousands of rows of data. My task is to extract values from XML tags specified in Table1, in this case I want values from 3 xml tags: Client, FEE and ADDRESS.
So, if the XML is this
<XML>
<CLIENT>some client</CLIENT>
<FEE>some fee</FEE>
<ADDRESS>some address</ADDRESS>
</XML>
After running a query I should get this:
Client, FEE, ADDRESS
some client, some fee, some address
Right now i have a query:
SELECT
coalesce(Cast(ClientData as xml).value('(/XML/CLIENT)[1]', 'varchar(max)'), ''),
coalesce(Cast(ClientData as xml).value('(/XML/FEE)[1]', 'varchar(max)'), ''),
coalesce(Cast(ClientData as xml).value('(/XML/ADDRESS)[1]', 'varchar(max)'), '')
FROM dbo.Table2 WITH(NOLOCK)
This gives me the necessary result, however it is not dynamic. Meaning, if I want to include a 4th xml value, lets say, PHONE, I would need to add coalesce(Cast(ClientData as xml).value('(/XML/PHONE)[1]', 'varchar(max)'), '') to the SELECT
My question is,
How do I make my query dynamic so that instead of hardcoding tag names that I want to extract from XML in Table2 I would use Tabl1 as a source of tag names to extract?
I hope my explanation was good enough :)
Thank you!
You can achieve this using DYNAMIC SQL
The TagsTable should have all the possible Tags
we can then construct SQL using the tag names and execute it
create table TagsTable
( tagName varchar(256)
)
insert into TagsTable values ('CLIENT')
insert into TagsTable values ('FEE')
insert into TagsTable values ('ADDRESS')
declare #query nvarchar(max)
SELECT #query = STUFF((select ',' + 'coalesce(Cast(ClientData as xml).value(''(/XML/'
+ tagName + ')[1]'', ''varchar(max)''), '''') as ' + tagName +' '
FROM TagsTable
FOR XML PATH ('') ), 1,1,'')
SET #query = 'SELECT ' + #query + 'FROM dbo.Table2 WITH(NOLOCK)'
select #query
exec sp_executesql #query

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.

What is the best way to collapse the rows of a SELECT into a string?

In a SQL statement ( or procedure ) I want to collapse the rows of this table into a single comma delimited string.
simpleTable
id value
-- -----
1 "a"
2 "b"
3 "c"
Collapse to:
"a, b, c"
You can concatenate using an embedded 'set' statement in a query:
declare #combined varchar(2000)
select #combined = isnull(#combined + ', ','') + isnull(value,'')
from simpleTable
print #combined
(Note that the first isnull() initialises the string, and the second isnull() is especially important if there's any chance of nulls in the 'value' column, because otherwise a single null could wipe out the whole concatenation)
(edited code and explanation after comments)
Edit (ten years later):
SQL Server 2017 introduced the STRING_AGG() function which provides an official way of concatenating strings from different rows. Like other aggregation functions such as COUNT(), it can be used with GROUP BY.
So for the example above you could do:
select string_agg(value, ', ')
from simpleTable
If you had some other column and you wanted to concatenate for values of that column, you would add a 'group by' clause, e.g:
select someCategory, string_agg(value, ', ') as concatValues
from simpleTable
group by someCategory
Note string_agg will only work with SQL 2017 and above.
This will only work in MSSQL 2005+
select value + ',' from simpletable for xml path ('')
..one way to prevent the extra comma:
select case(row_number() over (order by id))
when 1 then value else ',' + value end
from simpletable
for xml path ('')
DECLARE #EmployeeList varchar(100)
SELECT #EmployeeList = COALESCE(#EmployeeList + ', ', '') +
CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
SELECT #EmployeeList
Results:
1, 2, 4
This is based on #codeulike answer, but will prevent losing the portion of the string that gets concatenated before a null "value" is concatenated on.
declare #combined varchar(2000)
select #combined = isnull(#combined + ', ','') + ISNULL(value,'')
from simpleTable
print #combined