关于tsql:使用逗号作为分隔符将多行转换为一行

Convert multiple rows into one with comma as separator

本问题已经有最佳答案,请猛点这里访问。

如果我发布SELECT username FROM Users我会得到这个结果:

1
2
3
4
5
username
--------
Paul
John
Mary

但我真正需要的是一行,所有值都用逗号分隔,如下所示:

1
Paul, John, Mary

我该怎么做?


1
2
3
4
5
6
7
8
9
10
11
 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

以前有打字错误,上面的工作


这应该对你有用。一直测试到SQL 2000。

1
2
3
4
5
6
7
8
9
10
11
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))


很好地回顾了几种方法:

http://blogs.msmvps.com/robfarley/2007/04/07/coalesce-is-not-the-answer-to-string-concatation-in-t-sql/

文章拷贝-

Coalesce并不是T-SQL中字符串连接的答案。多年来,我看到过很多关于使用Coalesce函数在T-SQL中实现字符串连接的文章。这是这里的一个例子(从readifarian marc ridey借来)。

1
2
3
4
5
6
7
DECLARE @categories varchar(200)
SET @categories = NULL

SELECT @categories = COALESCE(@categories + ',','') + Name
FROM Production.ProductCategory

SELECT @categories

这个查询可能非常有效,但是需要小心,并且应该正确理解联合的使用。Coalesce是isNull的版本,它可以接受两个以上的参数。它返回参数列表中不为空的第一项。所以实际上,它与串联没有任何关系,下面的代码是完全相同的——不使用coalesce:

1
2
3
4
5
6
7
DECLARE @categories varchar(200)
SET @categories = ''

SELECT @categories = @categories + ',' + Name
FROM Production.ProductCategory

SELECT @categories

但是数据库的无序性使得这不可靠。T-SQL没有(还没有)连接函数的全部原因是,这是一个元素顺序很重要的聚合。使用这个字符串串联的变量分配方法,您可能会发现返回的答案中没有所有的值,特别是如果您希望子字符串按特定的顺序排列的话。考虑以下几点,我的机器上只有"附件"在我想要它返回时才返回,自行车、衣服、部件、附件:

1
2
3
4
5
6
7
8
DECLARE @categories varchar(200)
SET @categories = NULL

SELECT @categories = COALESCE(@categories + ',','') + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)

SELECT @categories

更好的方法是使用一种方法,该方法确实考虑了顺序,并且已经包含在sql2005中,专门用于字符串串联-用于XML路径("")

1
2
3
4
SELECT ',' + Name
FROM Production.ProductCategory
ORDER BY LEN(Name)
FOR XML PATH('')

在我最近在使用子查询比较group by和distinct的文章中,我演示了for xml路径("")的用法。看看这个,你会发现它在子查询中是如何工作的。"stuff"函数只用于删除前导逗号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
;

FORXML路径是子查询中唯一可以使用ORDERBY的情况之一。另一个是顶部。当您使用一个未命名的列和XML路径("")时,您将得到一个没有XML标记的直接连接。这确实意味着字符串将被HTML编码,因此,如果您要连接可能包含<字符(etc)的字符串,那么您可以在以后修复它,但无论如何,这仍然是SQL Server 2005中连接字符串的最佳方法。


基于Mwigdahls的答案。如果你还需要做分组,这里是如何让它看起来像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

您可以使用此查询执行上述任务:

1
2
3
DECLARE @test NVARCHAR(max)  
SELECT @test = COALESCE(@test + ',', '') + field2 FROM #test
SELECT field2 = @test

有关详细信息和逐步说明,请访问以下链接http://oops-solution.blogspot.com/2011/11/sql-server-convert-table-column-data.html


1
2
3
4
5
6
7
8
DECLARE @EmployeeList varchar(100)

SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') +
   CAST(Emp_UniqueID AS varchar(5))
FROM SalesCallsEmployees
WHERE SalCal_UniqueID = 1

SELECT @EmployeeList

来源:http://www.sqlteam.com/article/using-coalesce-to-build-comma-delimited-string


在sqlite中,这更简单。我认为mysql、mssql和orable有类似的实现

1
2
3
4
5
6
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;

在MS SQL Server 2005/2008中,一个干净而灵活的解决方案是创建一个clr agregate函数。

你会在谷歌上找到很多文章(带代码)。

看起来这篇文章使用C引导您完成整个过程。


可以使用stuff()将行转换为逗号分隔的值

1
2
3
4
5
6
7
8
9
10
11
12
13
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;

感谢@alexkuznetsov提供此答案的参考。


如果您是通过php来执行这个操作,那么这个呢?

1
2
3
4
5
6
$hQuery = mysql_query("SELECT * FROM users");
while($hRow = mysql_fetch_array($hQuery)) {
    $hOut .= $hRow['username'] .",";
}
$hOut = substr($hOut, 0, strlen($hOut) - 1);
echo $hOut;