Separated by Comma in SQL Server [duplicate] - sql-server

Consider a database table holding names, with three rows:
Peter
Paul
Mary
Is there an easy way to turn this into a single string of Peter, Paul, Mary?

If you are on SQL Server 2017 or Azure, see Mathieu Renda answer.
I had a similar issue when I was trying to join two tables with one-to-many relationships. In SQL 2005 I found that XML PATH method can handle the concatenation of the rows very easily.
If there is a table called STUDENTS
SubjectID StudentName
---------- -------------
1 Mary
1 John
1 Sam
2 Alaina
2 Edward
Result I expected was:
SubjectID StudentName
---------- -------------
1 Mary, John, Sam
2 Alaina, Edward
I used the following T-SQL:
SELECT Main.SubjectID,
LEFT(Main.Students,Len(Main.Students)-1) As "Students"
FROM
(
SELECT DISTINCT ST2.SubjectID,
(
SELECT ST1.StudentName + ',' AS [text()]
FROM dbo.Students ST1
WHERE ST1.SubjectID = ST2.SubjectID
ORDER BY ST1.SubjectID
FOR XML PATH (''), TYPE
).value('text()[1]','nvarchar(max)') [Students]
FROM dbo.Students ST2
) [Main]
You can do the same thing in a more compact way if you can concat the commas at the beginning and use substring to skip the first one so you don't need to do a sub-query:
SELECT DISTINCT ST2.SubjectID,
SUBSTRING(
(
SELECT ','+ST1.StudentName AS [text()]
FROM dbo.Students ST1
WHERE ST1.SubjectID = ST2.SubjectID
ORDER BY ST1.SubjectID
FOR XML PATH (''), TYPE
).value('text()[1]','nvarchar(max)'), 2, 1000) [Students]
FROM dbo.Students ST2

This answer may return unexpected results For consistent results, use one of the FOR XML PATH methods detailed in other answers.
Use COALESCE:
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + ', ', '') + Name
FROM People
Just some explanation (since this answer seems to get relatively regular views):
Coalesce is really just a helpful cheat that accomplishes two things:
1) No need to initialize #Names with an empty string value.
2) No need to strip off an extra separator at the end.
The solution above will give incorrect results if a row has a NULL Name value (if there is a NULL, the NULL will make #Names NULL after that row, and the next row will start over as an empty string again. Easily fixed with one of two solutions:
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + ', ', '') + Name
FROM People
WHERE Name IS NOT NULL
or:
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(#Names + ', ', '') +
ISNULL(Name, 'N/A')
FROM People
Depending on what behavior you want (the first option just filters NULLs out, the second option keeps them in the list with a marker message [replace 'N/A' with whatever is appropriate for you]).

SQL Server 2017+ and SQL Azure: STRING_AGG
Starting with the next version of SQL Server, we can finally concatenate across rows without having to resort to any variable or XML witchery.
STRING_AGG (Transact-SQL)
Without grouping
SELECT STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department;
With grouping:
SELECT GroupName, STRING_AGG(Name, ', ') AS Departments
FROM HumanResources.Department
GROUP BY GroupName;
With grouping and sub-sorting
SELECT GroupName, STRING_AGG(Name, ', ') WITHIN GROUP (ORDER BY Name ASC) AS Departments
FROM HumanResources.Department
GROUP BY GroupName;

One method not yet shown via the XML data() command in SQL Server is:
Assume a table called NameList with one column called FName,
SELECT FName + ', ' AS 'data()'
FROM NameList
FOR XML PATH('')
returns:
"Peter, Paul, Mary, "
Only the extra comma must be dealt with.
As adopted from #NReilingh's comment, you can use the following method to remove the trailing comma. Assuming the same table and column names:
STUFF(REPLACE((SELECT '#!' + LTRIM(RTRIM(FName)) AS 'data()' FROM NameList
FOR XML PATH('')),' #!',', '), 1, 2, '') as Brands

In SQL Server 2005
SELECT Stuff(
(SELECT N', ' + Name FROM Names FOR XML PATH(''),TYPE)
.value('text()[1]','nvarchar(max)'),1,2,N'')
In SQL Server 2016
you can use the FOR JSON syntax
i.e.
SELECT per.ID,
Emails = JSON_VALUE(
REPLACE(
(SELECT _ = em.Email FROM Email em WHERE em.Person = per.ID FOR JSON PATH)
,'"},{"_":"',', '),'$[0]._'
)
FROM Person per
And the result will become
Id Emails
1 abc#gmail.com
2 NULL
3 def#gmail.com, xyz#gmail.com
This will work even your data contains invalid XML characters
the '"},{"_":"' is safe because if you data contain '"},{"_":"', it will be escaped to "},{\"_\":\"
You can replace ', ' with any string separator
And in SQL Server 2017, Azure SQL Database
You can use the new STRING_AGG function

In MySQL, there is a function, GROUP_CONCAT(), which allows you to concatenate the values from multiple rows. Example:
SELECT 1 AS a, GROUP_CONCAT(name ORDER BY name ASC SEPARATOR ', ') AS people
FROM users
WHERE id IN (1,2,3)
GROUP BY a

Use COALESCE - Learn more from here
For an example:
102
103
104
Then write the below code in SQL Server,
Declare #Numbers AS Nvarchar(MAX) -- It must not be MAX if you have few numbers
SELECT #Numbers = COALESCE(#Numbers + ',', '') + Number
FROM TableName where Number IS NOT NULL
SELECT #Numbers
The output would be:
102,103,104

PostgreSQL arrays are awesome. Example:
Create some test data:
postgres=# \c test
You are now connected to database "test" as user "hgimenez".
test=# create table names (name text);
CREATE TABLE
test=# insert into names (name) values ('Peter'), ('Paul'), ('Mary');
INSERT 0 3
test=# select * from names;
name
-------
Peter
Paul
Mary
(3 rows)
Aggregate them in an array:
test=# select array_agg(name) from names;
array_agg
-------------------
{Peter,Paul,Mary}
(1 row)
Convert the array to a comma-delimited string:
test=# select array_to_string(array_agg(name), ', ') from names;
array_to_string
-------------------
Peter, Paul, Mary
(1 row)
DONE
Since PostgreSQL 9.0 it is even easier, quoting from deleted answer by "horse with no name":
select string_agg(name, ',')
from names;

Oracle 11g Release 2 supports the LISTAGG function. Documentation here.
COLUMN employees FORMAT A50
SELECT deptno, LISTAGG(ename, ',') WITHIN GROUP (ORDER BY ename) AS employees
FROM emp
GROUP BY deptno;
DEPTNO EMPLOYEES
---------- --------------------------------------------------
10 CLARK,KING,MILLER
20 ADAMS,FORD,JONES,SCOTT,SMITH
30 ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD
3 rows selected.
Warning
Be careful implementing this function if there is possibility of the resulting string going over 4000 characters. It will throw an exception. If that's the case then you need to either handle the exception or roll your own function that prevents the joined string from going over 4000 characters.

In SQL Server 2005 and later, use the query below to concatenate the rows.
DECLARE #t table
(
Id int,
Name varchar(10)
)
INSERT INTO #t
SELECT 1,'a' UNION ALL
SELECT 1,'b' UNION ALL
SELECT 2,'c' UNION ALL
SELECT 2,'d'
SELECT ID,
stuff(
(
SELECT ','+ [Name] FROM #t WHERE Id = t.Id FOR XML PATH('')
),1,1,'')
FROM (SELECT DISTINCT ID FROM #t ) t

A recursive CTE solution was suggested, but no code was provided. The code below is an example of a recursive CTE.
Note that although the results match the question, the data doesn't quite match the given description, as I assume that you really want to be doing this on groups of rows, not all rows in the table. Changing it to match all rows in the table is left as an exercise for the reader.
;WITH basetable AS (
SELECT
id,
CAST(name AS VARCHAR(MAX)) name,
ROW_NUMBER() OVER (Partition BY id ORDER BY seq) rw,
COUNT(*) OVER (Partition BY id) recs
FROM (VALUES
(1, 'Johnny', 1),
(1, 'M', 2),
(2, 'Bill', 1),
(2, 'S.', 4),
(2, 'Preston', 5),
(2, 'Esq.', 6),
(3, 'Ted', 1),
(3, 'Theodore', 2),
(3, 'Logan', 3),
(4, 'Peter', 1),
(4, 'Paul', 2),
(4, 'Mary', 3)
) g (id, name, seq)
),
rCTE AS (
SELECT recs, id, name, rw
FROM basetable
WHERE rw = 1
UNION ALL
SELECT b.recs, r.ID, r.name +', '+ b.name name, r.rw + 1
FROM basetable b
INNER JOIN rCTE r ON b.id = r.id AND b.rw = r.rw + 1
)
SELECT name
FROM rCTE
WHERE recs = rw AND ID=4
OPTION (MAXRECURSION 101)

I don't have access to a SQL Server at home, so I'm guess at the syntax here, but it's more or less:
DECLARE #names VARCHAR(500)
SELECT #names = #names + ' ' + Name
FROM Names

In SQL Server 2017 or later versions, you can use the STRING_AGG() function to generate comma-separated values. Please have a look below at one example.
SELECT
VendorId, STRING_AGG(FirstName,',') UsersName
FROM Users
WHERE VendorId != 9
GROUP BY VendorId

You need to create a variable that will hold your final result and select into it, like so.
Easiest Solution
DECLARE #char VARCHAR(MAX);
SELECT #char = COALESCE(#char + ', ' + [column], [column])
FROM [table];
PRINT #char;

In SQL Server vNext this will be built in with the STRING_AGG function. Read more about it in STRING_AGG (Transact-SQL).

A ready-to-use solution, with no extra commas:
select substring(
(select ', '+Name AS 'data()' from Names for xml path(''))
,3, 255) as "MyList"
An empty list will result in NULL value.
Usually you will insert the list into a table column or program variable: adjust the 255 max length to your need.
(Diwakar and Jens Frandsen provided good answers, but need improvement.)

This worked for me (SQL Server 2016):
SELECT CarNamesString = STUFF((
SELECT ',' + [Name]
FROM tbl_cars
FOR XML PATH('')
), 1, 1, '')
Here is the source: https://www.mytecbits.com/
And a solution for MySQL (since this page show up in Google for MySQL):
SELECT [Name],
GROUP_CONCAT(DISTINCT [Name] SEPARATOR ',')
FROM tbl_cars
From MySQL documentation.

Using XML helped me in getting rows separated with commas. For the extra comma we can use the replace function of SQL Server. Instead of adding a comma, use of the AS 'data()' will concatenate the rows with spaces, which later can be replaced with commas as the syntax written below.
REPLACE(
(select FName AS 'data()' from NameList for xml path(''))
, ' ', ', ')

SELECT STUFF((SELECT ', ' + name FROM [table] FOR XML PATH('')), 1, 2, '')
Here's a sample:
DECLARE #t TABLE (name VARCHAR(10))
INSERT INTO #t VALUES ('Peter'), ('Paul'), ('Mary')
SELECT STUFF((SELECT ', ' + name FROM #t FOR XML PATH('')), 1, 2, '')
--Peter, Paul, Mary

With the other answers, the person reading the answer must be aware of a specific domain table such as vehicle or student. The table must be created and populated with data to test a solution.
Below is an example that uses SQL Server "Information_Schema.Columns" table. By using this solution, no tables need to be created or data added. This example creates a comma separated list of column names for all tables in the database.
SELECT
Table_Name
,STUFF((
SELECT ',' + Column_Name
FROM INFORMATION_SCHEMA.Columns Columns
WHERE Tables.Table_Name = Columns.Table_Name
ORDER BY Column_Name
FOR XML PATH ('')), 1, 1, ''
)Columns
FROM INFORMATION_SCHEMA.Columns Tables
GROUP BY TABLE_NAME

On top of Chris Shaffer's answer:
If your data may get repeated, such as
Tom
Ali
John
Ali
Tom
Mike
Instead of having Tom,Ali,John,Ali,Tom,Mike
You can use DISTINCT to avoid duplicates and get Tom,Ali,John,Mike:
DECLARE #Names VARCHAR(8000)
SELECT DISTINCT #Names = COALESCE(#Names + ',', '') + Name
FROM People
WHERE Name IS NOT NULL
SELECT #Names

MySQL complete example:
We have users who can have much data and we want to have an output, where we can see all users' data in a list:
Result:
___________________________
| id | rowList |
|-------------------------|
| 0 | 6, 9 |
| 1 | 1,2,3,4,5,7,8,1 |
|_________________________|
Table Setup:
CREATE TABLE `Data` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
INSERT INTO `Data` (`id`, `user_id`) VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1),
(6, 0),
(7, 1),
(8, 1),
(9, 0),
(10, 1);
CREATE TABLE `User` (
`id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `User` (`id`) VALUES
(0),
(1);
Query:
SELECT User.id, GROUP_CONCAT(Data.id ORDER BY Data.id) AS rowList FROM User LEFT JOIN Data ON User.id = Data.user_id GROUP BY User.id

DECLARE #Names VARCHAR(8000)
SELECT #name = ''
SELECT #Names = #Names + ',' + Names FROM People
SELECT SUBSTRING(2, #Names, 7998)
This puts the stray comma at the beginning.
However, if you need other columns, or to CSV a child table you need to wrap this in a scalar user defined field (UDF).
You can use XML path as a correlated subquery in the SELECT clause too (but I'd have to wait until I go back to work because Google doesn't do work stuff at home :-)

To avoid null values you can use CONCAT()
DECLARE #names VARCHAR(500)
SELECT #names = CONCAT(#names, ' ', name)
FROM Names
select #names

I really liked elegancy of Dana's answer and just wanted to make it complete.
DECLARE #names VARCHAR(MAX)
SET #names = ''
SELECT #names = #names + ', ' + Name FROM Names
-- Deleting last two symbols (', ')
SET #sSql = LEFT(#sSql, LEN(#sSql) - 1)

If you want to deal with nulls you can do it by adding a where clause or add another COALESCE around the first one.
DECLARE #Names VARCHAR(8000)
SELECT #Names = COALESCE(COALESCE(#Names + ', ', '') + Name, #Names) FROM People

This answer will require some privilege on the server to work.
Assemblies are a good option for you. There are a lot of sites that explain how to create it. The one I think is very well explained is this one.
If you want, I have already created the assembly, and it is possible to download the DLL file here.
Once you have downloaded it, you will need to run the following script in your SQL Server:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE;
EXEC sp_configure 'clr strict security', 1;
RECONFIGURE;
CREATE Assembly concat_assembly
AUTHORIZATION dbo
FROM '<PATH TO Concat.dll IN SERVER>'
WITH PERMISSION_SET = SAFE;
GO
CREATE AGGREGATE dbo.concat (
#Value NVARCHAR(MAX)
, #Delimiter NVARCHAR(4000)
) RETURNS NVARCHAR(MAX)
EXTERNAL Name concat_assembly.[Concat.Concat];
GO
sp_configure 'clr enabled', 1;
RECONFIGURE
Observe that the path to assembly may be accessible to server. Since you have successfully done all the steps, you can use the function like:
SELECT dbo.Concat(field1, ',')
FROM Table1
Since SQL Server 2017 it is possible to use the STRING_AGG function.

I usually use select like this to concatenate strings in SQL Server:
with lines as
(
select
row_number() over(order by id) id, -- id is a line id
line -- line of text.
from
source -- line source
),
result_lines as
(
select
id,
cast(line as nvarchar(max)) line
from
lines
where
id = 1
union all
select
l.id,
cast(r.line + N', ' + l.line as nvarchar(max))
from
lines l
inner join
result_lines r
on
l.id = r.id + 1
)
select top 1
line
from
result_lines
order by
id desc

In Oracle, it is wm_concat. I believe this function is available in the 10g release and higher.

For Oracle DBs, see this question: How can multiple rows be concatenated into one in Oracle without creating a stored procedure?
The best answer appears to be by #Emmanuel, using the built-in LISTAGG() function, available in Oracle 11g Release 2 and later.
SELECT question_id,
LISTAGG(element_id, ',') WITHIN GROUP (ORDER BY element_id)
FROM YOUR_TABLE;
GROUP BY question_id
as #user762952 pointed out, and according to Oracle's documentation http://www.oracle-base.com/articles/misc/string-aggregation-techniques.php, the WM_CONCAT() function is also an option. It seems stable, but Oracle explicitly recommends against using it for any application SQL, so use at your own risk.
Other than that, you will have to write your own function; the Oracle document above has a guide on how to do that.

Related

CONCAT multiple fields which may have NULLS, set the delimiter once: SQL Server 2008 R2

I would like to concatenate many columns in SQL Server 2008 R2, and have them delimited by a TAB. So far, I have only been able to come up with a primitive solution:
select
isnull(col1,'')
+ CHAR(9)
+ isnull(col2,'')
+ CHAR(9)
+ isnull(col3,'')
+ CHAR(9)
+ isnull(col4,'')
+ CHAR(9)
+ isnull(col5,'')
from TBL_A
For the sake of brevity, I only included 5 columns, however I need to concatenate about 20 columns.
Since any of the columns could be NULL, I need to wrap each columns in the isnull statement. I also need to add the char(9) after every column. CONCAT_WS would take care of both of these issues very simply however this is only available in SQL Server 2017.
Does anyone have a more eloquent way of doing this?
I feel like a STUFF() in combination with FOR XML PATH() and an UNPIVOT should get this done for you. I don't think there's a way around the multiple null checks.
Here is a working fiddle of how you might accomplish this.
-- schema
create table t1 (
id int
,col1 char(1)
,col2 char(1)
,col3 char(1)
);
create table t2
(
id int,
colVal char(1)
);
-- some sample data
insert into t1
select 1, 'a', 'b', 'c'
union all
select 2, 'd', 'e', 'f';
insert into t2
select
id
,colVal
from t1
unpivot
(
colVal
for cols in (col1, col2, col3)
) u
order by
id;
-- the actual query
select
id
,stuff((
select
'-'
+ isnull(colVal,'')
from
t2 as innerTable
where
innerTable.id = outerTable.id
for xml path (''))
,1 , 1, '')
from
t2 as outerTable
group by
id;
drop table t1;
drop table t2;
The process here is as follows:
"Verticalize" the data using UNPIVOT and the row identifier to allow us to aggregate it back into row groups later on.
Use a correlated subquery to query against the vertical data per group.
Using FOR XML PATH('') to combine the record values into a delimited string and STUFF to chop off the leading separator.
Note that I've used dashes in my example to make it clearer how the separators are coming into play. I also made the presumption that your records have some form of unique identifier (id int in the example).

How to use a subquery to insert comma separated values into a temporary table

I'm trying to populate the Employees field in the temporary table using a subquery that comma separates the employee ids. My syntax is not correct.
What is the correct syntax to get the comma separated employee ids to insert into the temporary table? Here is my code:
declare #CommaSeperatedList varchar(max)
create table #tmp
(
ManagerId int,
Employees varchar(max)
)
insert into #tmp (ManagerId, Employees)
select
m.Id,
select #CommaSeperatedList = COALESCE(#CommaSeperatedList + ', ', '') + cast(emp.Id as varchar from emp select #CommaSeperatedList
from Manager m
inner join Employee emp on
m.EmployeeId = emp.Id
Unless you are using SQL Azure or SQL Server 2017, which have the new STRING_AGG function, the best option is to use the FOR XML PATH('') method.
It looks like the following...
SELECT
e1.mgrid,
Employees = STUFF((
SELECT
CONCAT(', ', e2.empid)
FROM
HR.Employees e2
WHERE
e1.mgrid = e2.mgrid
FOR XML PATH('')
), 1, 2, '')
FROM
HR.Employees e1
WHERE
e1.mgrid IS NOT NULL
GROUP BY
e1.mgrid;
HTH,
Jason
You can use these examples on how to concat your rows. Be carefull of using XML PATH - It cant kill your CPU Performance. But if it isnt too many rows, i would go for XML PATH.
Look for more options here https://www.red-gate.com/simple-talk/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
Example could be like this:
SELECT ',' + SUB.Name AS [text()] FROM Production.ProductSubcategory SUB
WHERE SUB.ProductCategoryID = CAT.ProductCategoryID FOR XML PATH('') , 1, 1, '' ) AS [Sub Categories] FROM Production.ProductCategory CAT
 

MS SQL Split a column value using delimiters and update in other field

Ex, I have a table like this,
ID Name
1 Apple,banana
2 Grape,Orange
3 Papaya,Jackfruit
I need to split (,) and save like this in SQL
ID Name Name2
1 Apple banana
2 Grape Orange
3 Papaya Jackfruit
The fastest, most scaleable way to split strings before SQL Server 2016 is to write a SQLCLR method that splits strings, like this one. SQL Server 2016 introduced the STRING_SPLIT function which is even faster.
The second fastest way to split strings for versions before SQL Server 2016 is to convert the separated text into XML and use XML operators to retrieve the individual items. The typical usage, as shown in Aaron Bertrand's articles returns items as rows. It can be adapted easily to return items as columns:
declare #table table (ID int, Name nvarchar(200))
insert into #table
values
(1,'Apple,banana'),
(2,'Grape,Orange'),
(3,'Papaya,Jackfruit');
with items as (
select
ID,
xmlField= cast('<item><tag>'
+ replace(Name,',','</tag><tag>')
+ '</tag></item>' as xml)
from #table
)
-- Step 2: Select different tags and display them as fields
select
y.item.value('(tag/text())[1]','nvarchar(20)') As Name1,
y.item.value('(tag/text())[2]','nvarchar(20)') as Name2
from items outer apply xmlField.nodes('item') as y(item)
This returns :
1 Apple banana
2 Grape Orange
3 Papaya Jackfruit
This works by first converting Name1,Name2 to <item><tag>Name1</tag><tag>Name2</tag><item> which can be cast to XML and returned as xmlField.
outer apply xmlField.nodes('item') as y(item) converts this field to a table of items named y. Only one item row exists in each field.
Finally, y.item.value('(tag/text())[1]','nvarchar(20)') extracts the text of the first tag element as Name1.
This can be extended easily to multiple entries, or to return entries as different elements.
The number of columns has to be known in advance. SQL, the language, doesn't allow an arbitrary number of columns. If different fields contain a different number of tokens, they'll have to be returned as rows.
In this case, you should use STRING_SPLIT if you target SQL Server 2016 or the original version of the XML splitting technique :
CREATE FUNCTION dbo.SplitStrings_XML
(
#List nvarchar(max),
#Delimiter nvarchar(10)
)
RETURNS TABLE WITH SCHEMABINDING
AS
RETURN (SELECT [value] = y.i.value('(./text())[1]', 'varchar(8000)')
FROM (SELECT x = CONVERT(XML, '<i>'
+ REPLACE(#List, #Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i));
It's worth checking Performance Surprises and Assumptions : STRING_SPLIT() which compares all available string splitting techniques to find the fastest and most scaleable
We get the Result by using Lead and Lag Function along with Split String And insert the Result set in to your table as You Required
IF OBJECT_ID('tempdb..#InsertTable') IS NOT NULL
DROP TABLE #InsertTable
DECLARE #table TABLE (ID INT, Name VARCHAR(50))
CREATE TABLE #InsertTable (ID INT,Name1 VARCHAR(100),Name2 VARCHAR(100))
INSERT INTO #table
SELECT 1,'Apple,Banana' UNION ALL
SELECT 2,'Grape,Orange' UNION ALL
SELECT 3,'Papaya,Jackfruit'
INSERT INTO #InsertTable(ID,Name1,Name2)
SELECT DISTINCT ID,
ISNULL(Name1,LagName1) AS Name1 ,
ISNULL(Name2,LeadName2) AS Name2
FROM
(
SELECT ID,
Name1,
LAG(NAme1,1)OVER(ORDER BY ID) LagName1,
Name2,
LEAD(Name2,1)OVER(ORDER BY ID)LeadName2
FROM
(
SELECT ID, CASE WHEN Seq%2=1 THEN Name END AS Name1,
CASE WHEN Seq%2=0 THEN Name END AS Name2
FROM
(
SELECT Row_NUmber ()OVER(ORDER BY ID )AS Seq,ID, Split.a.value('.', 'VARCHAR(1000)') AS Name
FROM (
SELECT ID, CAST('<S>' + REPLACE(Name, ',', '</S><S>') + '</S>' AS XML) AS Name
FROM #table
) AS A
CROSS APPLY Name.nodes('/S') AS Split(a)
)Dt
)DT2
)Final
SELECT * FROM #InsertTable
Result
ID Name1 Name2
----------------------
1 Apple Banana
2 Grape Orange
3 Papaya Jackfruit

How to create comma delimited list from table with dynamic columns

I want to be able to grab all of the records in a table into a comma delimited list that I can then use to insert into a table on another database. Due to permission restrictions on the customer's server I cannot access any of the options when right-clicking on the database name, and all of the solutions I've found so far involve having permission to do so (e.g. Tasks > Export Data...)
I have tried using COALESCE to do this, however the problem is that my table could have any number of columns. Columns can be added/deleted at any time through the UI by the users and therefore I cannot hard code the columns in my select statement.
Here is what I have written so far, using a simple CTE statement where there are three columns (RowCode, RowOrd, RowText) and concatenating them into a variable that I print out. I just want to find a way to grab these column names dynamically instead of hard coding them. I'll also need to account for various types of column names by casting them each as varchar in the variable.
DECLARE #listStr VARCHAR(MAX)
;WITH tableData AS
(
SELECT *
FROM tableRows
)
SELECT
#listStr = ISNULL(#listStr + 'select ','select ') + '''' + RowCode + ''',''' + cast(RowOrd as varchar) + ''',''' + RowText + '''' + Char(13)
FROM
tableData
PRINT #listStr
The tableRows table contains the following records
RowCode RowOrd RowText
-----------------------
RowA 1 Row A
RowB 2 Row B
And the variable #listStr is currently printing this, which is correct
select 'RowA','1.00','Row A'
select 'RowB','2.00','Row B'
Thanks in advance!
With a bit of XML you can dynamically gather and "stringify" your values
Declare #tableRows table (RowCode varchar(50), RowOrd int, RowText varchar(50))
Insert Into #tableRows values
('RowA',1,'Row A'),
('RowB',2,'Row B')
Declare #listStr VARCHAR(MAX) = ''
Select #listStr = #listStr + C.String + char(13)
From #tableRows A
Cross Apply (Select XMLData = cast((Select A.* for XML RAW) as xml)) B
Cross Apply (
Select String = 'select '+Stuff((Select ',' +Value
From (
Select Value = ''''+attr.value('.','varchar(max)')+''''
From B.XMLData.nodes('/row') as A(r)
Cross Apply A.r.nodes('./#*') AS B(attr)
) X
For XML Path ('')),1,1,'')
) C
Select #listStr
Returns
select 'RowA','1','Row A'
select 'RowB','2','Row B'

Stored procedure: Return column values as comma separated values [duplicate]

This question already has answers here:
How to concatenate text from multiple rows into a single text string in SQL Server
(47 answers)
Closed 7 years ago.
If I issue SELECT username FROM Users I get this result:
username
--------
Paul
John
Mary
but what I really need is one row with all the values separated by comma, like this:
Paul, John, Mary
How do I do this?
select
distinct
stuff((
select ',' + u.username
from users u
where u.username = username
order by u.username
for xml path('')
),1,1,'') as userlist
from users
group by username
had a typo before, the above works
This should work for you. Tested all the way back to SQL 2000.
create table #user (username varchar(25))
insert into #user (username) values ('Paul')
insert into #user (username) values ('John')
insert into #user (username) values ('Mary')
declare #tmp varchar(250)
SET #tmp = ''
select #tmp = #tmp + username + ', ' from #user
select SUBSTRING(#tmp, 0, LEN(#tmp))
good review of several approaches:
http://blogs.msmvps.com/robfarley/2007/04/07/coalesce-is-not-the-answer-to-string-concatentation-in-t-sql/
Article copy -
Coalesce is not the answer to string concatentation in T-SQL I've seen many posts over the years about using the COALESCE function to get string concatenation working in T-SQL. This is one of the examples here (borrowed from Readifarian Marc Ridey).
DECLARE #categories varchar(200)
SET #categories = NULL
SELECT #categories = COALESCE(#categories + ',','') + Name
FROM Production.ProductCategory
SELECT #categories
This query can be quite effective, but care needs to be taken, and the use of COALESCE should be properly understood. COALESCE is the version of ISNULL which can take more than two parameters. It returns the first thing in the list of parameters which is not null. So really it has nothing to do with concatenation, and the following piece of code is exactly the same - without using COALESCE:
DECLARE #categories varchar(200)
SET #categories = ''
SELECT #categories = #categories + ',' + Name
FROM Production.ProductCategory
SELECT #categories
But the unordered nature of databases makes this unreliable. The whole reason why T-SQL doesn't (yet) have a concatenate function is that this is an aggregate for which the order of elements is important. Using this variable-assignment method of string concatenation, you may actually find that the answer that gets returned doesn't have all the values in it, particularly if you want the substrings put in a particular order. Consider the following, which on my machine only returns ',Accessories', when I wanted it to return ',Bikes,Clothing,Components,Accessories':
DECLARE #categories varchar(200)
SET #categories = NULL
SELECT #categories = COALESCE(#categories + ',','') + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)
SELECT #categories
Far better is to use a method which does take order into consideration, and which has been included in SQL2005 specifically for the purpose of string concatenation - FOR XML PATH('')
SELECT ',' + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)
FOR XML PATH('')
In the post I made recently comparing GROUP BY and DISTINCT when using subqueries, I demonstrated the use of FOR XML PATH(''). Have a look at this and you'll see how it works in a subquery. The 'STUFF' function is only there to remove the leading comma.
USE tempdb;
GO
CREATE TABLE t1 (id INT, NAME VARCHAR(MAX));
INSERT t1 values (1,'Jamie');
INSERT t1 values (1,'Joe');
INSERT t1 values (1,'John');
INSERT t1 values (2,'Sai');
INSERT t1 values (2,'Sam');
GO
select
id,
stuff((
select ',' + t.[name]
from t1 t
where t.id = t1.id
order by t.[name]
for xml path('')
),1,1,'') as name_csv
from t1
group by id
;
FOR XML PATH is one of the only situations in which you can use ORDER BY in a subquery. The other is TOP. And when you use an unnamed column and FOR XML PATH(''), you will get a straight concatenation, with no XML tags. This does mean that the strings will be HTML Encoded, so if you're concatenating strings which may have the < character (etc), then you should maybe fix that up afterwards, but either way, this is still the best way of concatenating strings in SQL Server 2005.
building on mwigdahls answer. if you also need to do grouping here is how to get it to look like
group, csv
'group1', 'paul, john'
'group2', 'mary'
--drop table #user
create table #user (groupName varchar(25), username varchar(25))
insert into #user (groupname, username) values ('apostles', 'Paul')
insert into #user (groupname, username) values ('apostles', 'John')
insert into #user (groupname, username) values ('family','Mary')
select
g1.groupname
, stuff((
select ', ' + g.username
from #user g
where g.groupName = g1.groupname
order by g.username
for xml path('')
),1,2,'') as name_csv
from #user g1
group by g1.groupname
You can use this query to do the above task:
DECLARE #test NVARCHAR(max)
SELECT #test = COALESCE(#test + ',', '') + field2 FROM #test
SELECT field2 = #test
For detail and step by step explanation visit the following link
http://oops-solution.blogspot.com/2011/11/sql-server-convert-table-column-data.html
DECLARE #EmployeeList varchar(100)
SELECT #EmployeeList = COALESCE(#EmployeeList + ', ', '') +
CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1
SELECT #EmployeeList
source:
http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string
In SQLite this is simpler. I think there are similar implementations for MySQL, MSSql and Orable
CREATE TABLE Beatles (id integer, name string );
INSERT INTO Beatles VALUES (1, "Paul");
INSERT INTO Beatles VALUES (2, "John");
INSERT INTO Beatles VALUES (3, "Ringo");
INSERT INTO Beatles VALUES (4, "George");
SELECT GROUP_CONCAT(name, ',') FROM Beatles;
you can use stuff() to convert rows as comma separated values
select
EmployeeID,
stuff((
SELECT ',' + FPProjectMaster.GroupName
FROM FPProjectInfo AS t INNER JOIN
FPProjectMaster ON t.ProjectID = FPProjectMaster.ProjectID
WHERE (t.EmployeeID = FPProjectInfo.EmployeeID)
And t.STatusID = 1
ORDER BY t.ProjectID
for xml path('')
),1,1,'') as name_csv
from FPProjectInfo
group by EmployeeID;
Thanks #AlexKuznetsov for the reference to get this answer.
A clean and flexible solution in MS SQL Server 2005/2008 is to create a CLR Agregate function.
You'll find quite a few articles (with code) on google.
It looks like this article walks you through the whole process using C#.
If you're executing this through PHP, what about this?
$hQuery = mysql_query("SELECT * FROM users");
while($hRow = mysql_fetch_array($hQuery)) {
$hOut .= $hRow['username'] . ", ";
}
$hOut = substr($hOut, 0, strlen($hOut) - 1);
echo $hOut;

Resources